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

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 (256) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +75 -0
  2. package/__tests__/DeviceCommands.test.ts +143 -7
  3. package/__tests__/RequestQueue.test.ts +106 -0
  4. package/__tests__/UploadPortfolio.test.ts +46 -0
  5. package/__tests__/core-dispose.test.ts +34 -0
  6. package/__tests__/core-error-output.test.ts +39 -0
  7. package/__tests__/device-lifecycle-events.test.ts +117 -0
  8. package/__tests__/device-pool-state.test.ts +20 -1
  9. package/__tests__/device-settings.test.ts +184 -6
  10. package/__tests__/device-state-events.test.ts +1 -2
  11. package/__tests__/device-state-mapper.test.ts +33 -26
  12. package/__tests__/device-state-store.test.ts +1 -34
  13. package/__tests__/device-utils.test.ts +106 -0
  14. package/__tests__/device-wallet-session-store.test.ts +65 -29
  15. package/__tests__/deviceSettings.test.ts +21 -12
  16. package/__tests__/evmLedgerLegacySafety.test.ts +2 -2
  17. package/__tests__/evmSignTypedData.test.ts +80 -0
  18. package/__tests__/filesystemValidation.test.ts +53 -0
  19. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +402 -0
  20. package/__tests__/get-device-state.test.ts +77 -41
  21. package/__tests__/logBlockEvent.test.ts +24 -11
  22. package/__tests__/networkUtils.test.ts +386 -0
  23. package/__tests__/open-wallet-session-error-response.test.ts +43 -0
  24. package/__tests__/open-wallet-session.test.ts +559 -0
  25. package/__tests__/protocol-v2.test.ts +756 -452
  26. package/__tests__/public-device-state-api.test.ts +113 -13
  27. package/__tests__/public-pro2-api-boundary.test.ts +91 -0
  28. package/__tests__/search-devices.test.ts +43 -0
  29. package/dist/api/BaseMethod.d.ts +3 -0
  30. package/dist/api/BaseMethod.d.ts.map +1 -1
  31. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  32. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  33. package/dist/api/ClearSessionCache.d.ts.map +1 -1
  34. package/dist/api/DirList.d.ts.map +1 -1
  35. package/dist/api/FileRead.d.ts.map +1 -1
  36. package/dist/api/FirmwareUpdateV2.d.ts +2 -2
  37. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  38. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  39. package/dist/api/FirmwareUpdateV4.d.ts +3 -2
  40. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  41. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  42. package/dist/api/OpenWalletSession.d.ts +14 -0
  43. package/dist/api/OpenWalletSession.d.ts.map +1 -0
  44. package/dist/api/PathInfo.d.ts.map +1 -1
  45. package/dist/api/PromptWebDeviceAccess.d.ts.map +1 -1
  46. package/dist/api/SearchDevices.d.ts +3 -0
  47. package/dist/api/SearchDevices.d.ts.map +1 -1
  48. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  49. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  50. package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
  51. package/dist/api/device/DeviceFullyUploadResource.d.ts +1 -2
  52. package/dist/api/device/DeviceFullyUploadResource.d.ts.map +1 -1
  53. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  54. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  55. package/dist/api/device/DeviceWipe.d.ts.map +1 -1
  56. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  57. package/dist/api/firmware/getBinary.d.ts +7 -3
  58. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  59. package/dist/api/helpers/filesystemValidation.d.ts +3 -0
  60. package/dist/api/helpers/filesystemValidation.d.ts.map +1 -1
  61. package/dist/api/helpers/paramsValidator.d.ts +1 -0
  62. package/dist/api/helpers/paramsValidator.d.ts.map +1 -1
  63. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  64. package/dist/api/index.d.ts +7 -11
  65. package/dist/api/index.d.ts.map +1 -1
  66. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  67. package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts.map +1 -1
  68. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +1 -1
  69. package/dist/api/utils.d.ts.map +1 -1
  70. package/dist/core/RequestQueue.d.ts +17 -1
  71. package/dist/core/RequestQueue.d.ts.map +1 -1
  72. package/dist/core/index.d.ts +3 -1
  73. package/dist/core/index.d.ts.map +1 -1
  74. package/dist/device/Device.d.ts +6 -5
  75. package/dist/device/Device.d.ts.map +1 -1
  76. package/dist/device/DeviceCommands.d.ts +5 -5
  77. package/dist/device/DeviceCommands.d.ts.map +1 -1
  78. package/dist/device/DevicePool.d.ts +1 -0
  79. package/dist/device/DevicePool.d.ts.map +1 -1
  80. package/dist/device/DeviceStateMapper.d.ts +2 -2
  81. package/dist/device/DeviceStateMapper.d.ts.map +1 -1
  82. package/dist/device/DeviceStateProjector.d.ts +1 -2
  83. package/dist/device/DeviceStateProjector.d.ts.map +1 -1
  84. package/dist/device/DeviceStateStore.d.ts +1 -3
  85. package/dist/device/DeviceStateStore.d.ts.map +1 -1
  86. package/dist/device/DeviceWalletSessionStore.d.ts +6 -1
  87. package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
  88. package/dist/device/cloneDeviceState.d.ts +2 -0
  89. package/dist/device/cloneDeviceState.d.ts.map +1 -0
  90. package/dist/deviceProfile/buildDeviceFeatures.d.ts +3 -1
  91. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  92. package/dist/events/logBlockEvent.d.ts.map +1 -1
  93. package/dist/index.d.ts +608 -75
  94. package/dist/index.js +2287 -1706
  95. package/dist/inject.d.ts.map +1 -1
  96. package/dist/protocols/protocol-v2/deviceSession.d.ts +7 -0
  97. package/dist/protocols/protocol-v2/deviceSession.d.ts.map +1 -0
  98. package/dist/protocols/protocol-v2/features.d.ts +11 -4
  99. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  100. package/dist/protocols/protocol-v2/index.d.ts +2 -2
  101. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  102. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +16 -0
  103. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -0
  104. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  105. package/dist/protocols/protocol-v2/walletSession.d.ts +8 -3
  106. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  107. package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -2
  108. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  109. package/dist/types/api/deviceSettings.d.ts +1 -0
  110. package/dist/types/api/deviceSettings.d.ts.map +1 -1
  111. package/dist/types/api/deviceUnlock.d.ts +1 -1
  112. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  113. package/dist/types/api/getFeatures.d.ts.map +1 -1
  114. package/dist/types/api/getOnekeyFeatures.d.ts.map +1 -1
  115. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  116. package/dist/types/api/index.d.ts +13 -15
  117. package/dist/types/api/index.d.ts.map +1 -1
  118. package/dist/types/api/openWalletSession.d.ts +41 -0
  119. package/dist/types/api/openWalletSession.d.ts.map +1 -0
  120. package/dist/types/api/protocolV2.d.ts +6 -11
  121. package/dist/types/api/protocolV2.d.ts.map +1 -1
  122. package/dist/types/device.d.ts +2 -9
  123. package/dist/types/device.d.ts.map +1 -1
  124. package/dist/utils/assets.d.ts +2 -1
  125. package/dist/utils/assets.d.ts.map +1 -1
  126. package/dist/utils/deviceFeaturesCompat.d.ts +13 -0
  127. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -0
  128. package/dist/utils/deviceFeaturesUtils.d.ts +11 -1
  129. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  130. package/dist/utils/deviceInfoUtils.d.ts +9 -8
  131. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  132. package/dist/utils/deviceSettings.d.ts +2 -0
  133. package/dist/utils/deviceSettings.d.ts.map +1 -1
  134. package/dist/utils/deviceVersionUtils.d.ts +6 -5
  135. package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
  136. package/dist/utils/index.d.ts +2 -2
  137. package/dist/utils/index.d.ts.map +1 -1
  138. package/dist/utils/networkUtils.d.ts +9 -1
  139. package/dist/utils/networkUtils.d.ts.map +1 -1
  140. package/dist/utils/patch.d.ts +1 -1
  141. package/dist/utils/patch.d.ts.map +1 -1
  142. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  143. package/package.json +4 -4
  144. package/src/api/BaseMethod.ts +17 -10
  145. package/src/api/CheckBootloaderRelease.ts +1 -2
  146. package/src/api/CheckFirmwareRelease.ts +1 -2
  147. package/src/api/ClearSessionCache.ts +0 -4
  148. package/src/api/DirList.ts +5 -3
  149. package/src/api/DirMake.ts +3 -3
  150. package/src/api/DirRemove.ts +3 -3
  151. package/src/api/FileDelete.ts +3 -3
  152. package/src/api/FileRead.ts +9 -6
  153. package/src/api/FileWrite.ts +3 -3
  154. package/src/api/FirmwareUpdate.ts +4 -4
  155. package/src/api/FirmwareUpdateV2.ts +133 -45
  156. package/src/api/FirmwareUpdateV3.ts +3 -5
  157. package/src/api/FirmwareUpdateV4.ts +192 -154
  158. package/src/api/GetPassphraseState.ts +10 -16
  159. package/src/api/OpenWalletSession.ts +221 -0
  160. package/src/api/PathInfo.ts +5 -3
  161. package/src/api/PromptWebDeviceAccess.ts +2 -3
  162. package/src/api/SearchDevices.ts +6 -2
  163. package/src/api/UploadPortfolio.ts +12 -3
  164. package/src/api/alephium/AlephiumSignTransaction.ts +2 -2
  165. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +4 -8
  166. package/src/api/benfen/BenfenGetAddress.ts +1 -1
  167. package/src/api/btc/BTCGetPublicKey.ts +1 -1
  168. package/src/api/device/DeviceChangePin.ts +1 -0
  169. package/src/api/device/DeviceFullyUploadResource.ts +4 -5
  170. package/src/api/device/DeviceSettings.ts +93 -0
  171. package/src/api/device/DeviceUpdateBootloader.ts +1 -2
  172. package/src/api/device/DeviceWipe.ts +1 -0
  173. package/src/api/evm/EVMSignTypedData.ts +3 -5
  174. package/src/api/evm/legacyV1/getAddress.ts +2 -2
  175. package/src/api/evm/legacyV1/getPublicKey.ts +2 -2
  176. package/src/api/evm/legacyV1/signMessage.ts +2 -2
  177. package/src/api/evm/legacyV1/signTypedData.ts +2 -2
  178. package/src/api/evm/legacyV1/signTypedHash.ts +2 -2
  179. package/src/api/evm/legacyV1/verifyMessage.ts +2 -2
  180. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +3 -3
  181. package/src/api/firmware/getBinary.ts +8 -3
  182. package/src/api/helpers/batchGetPublickeys.ts +2 -2
  183. package/src/api/helpers/filesystemValidation.ts +61 -0
  184. package/src/api/helpers/paramsValidator.ts +1 -1
  185. package/src/api/helpers/protocolV2FileWrite.ts +3 -2
  186. package/src/api/index.ts +9 -12
  187. package/src/api/nervos/NervosSignTransaction.ts +2 -2
  188. package/src/api/nexa/NexaSignTransaction.ts +1 -1
  189. package/src/api/protocol-v2/DeviceFactoryInfoGet.ts +1 -1
  190. package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +1 -1
  191. package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +4 -4
  192. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +6 -5
  193. package/src/api/protocol-v2/DeviceInfoGet.ts +4 -4
  194. package/src/api/protocol-v2/DeviceReboot.ts +1 -1
  195. package/src/api/protocol-v2/DeviceSettingsPageShow.ts +11 -8
  196. package/src/api/protocol-v2/DeviceSettingsSet.ts +4 -11
  197. package/src/api/protocol-v2/FilesystemFormat.ts +1 -1
  198. package/src/api/protocol-v2/FilesystemPermissionFix.ts +1 -1
  199. package/src/api/protocol-v2/Ping.ts +1 -1
  200. package/src/api/protocol-v2/ProtocolInfoRequest.ts +1 -1
  201. package/src/api/utils.ts +1 -3
  202. package/src/core/RequestQueue.ts +107 -6
  203. package/src/core/index.ts +91 -46
  204. package/src/data/messages/messages-protocol-v2.json +25 -79
  205. package/src/data-manager/DataManager.ts +6 -6
  206. package/src/device/Device.ts +186 -102
  207. package/src/device/DeviceCommands.ts +51 -21
  208. package/src/device/DeviceFeaturesState.ts +4 -4
  209. package/src/device/DevicePool.ts +17 -3
  210. package/src/device/DeviceStateMapper.ts +13 -29
  211. package/src/device/DeviceStateProjector.ts +11 -8
  212. package/src/device/DeviceStateStore.ts +11 -52
  213. package/src/device/DeviceWalletSessionStore.ts +31 -4
  214. package/src/device/cloneDeviceState.ts +39 -0
  215. package/src/device/deviceIdentity.ts +2 -2
  216. package/src/deviceProfile/buildDeviceFeatures.ts +29 -88
  217. package/src/events/logBlockEvent.ts +8 -1
  218. package/src/inject.ts +14 -24
  219. package/src/protocols/protocol-v2/deviceSession.ts +24 -0
  220. package/src/protocols/protocol-v2/features.ts +37 -49
  221. package/src/protocols/protocol-v2/firmware.ts +1 -1
  222. package/src/protocols/protocol-v2/index.ts +2 -3
  223. package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +61 -0
  224. package/src/protocols/protocol-v2/uiInteraction.ts +4 -3
  225. package/src/protocols/protocol-v2/walletSession.ts +57 -146
  226. package/src/types/api/checkAllFirmwareRelease.ts +1 -2
  227. package/src/types/api/deviceSettings.ts +1 -0
  228. package/src/types/api/deviceUnlock.ts +1 -1
  229. package/src/types/api/firmwareUpdate.ts +5 -5
  230. package/src/types/api/getDeviceState.ts +1 -1
  231. package/src/types/api/getFeatures.ts +3 -0
  232. package/src/types/api/getOnekeyFeatures.ts +3 -0
  233. package/src/types/api/getPassphraseState.ts +6 -0
  234. package/src/types/api/index.ts +16 -23
  235. package/src/types/api/openWalletSession.ts +64 -0
  236. package/src/types/api/protocolV2.ts +36 -18
  237. package/src/types/device.ts +20 -14
  238. package/src/types/params.ts +1 -1
  239. package/src/types/settings.ts +8 -8
  240. package/src/utils/assets.ts +4 -1
  241. package/src/utils/deviceFeaturesCompat.ts +173 -0
  242. package/src/utils/deviceFeaturesUtils.ts +9 -8
  243. package/src/utils/deviceInfoUtils.ts +26 -64
  244. package/src/utils/deviceSettings.ts +18 -11
  245. package/src/utils/deviceVersionUtils.ts +25 -44
  246. package/src/utils/findDefectiveBatchDevice.ts +3 -3
  247. package/src/utils/index.ts +2 -5
  248. package/src/utils/networkUtils.ts +270 -14
  249. package/src/utils/pro2Wallpaper.ts +11 -13
  250. package/src/utils/tracing.ts +2 -2
  251. package/dist/api/internal.d.ts +0 -4
  252. package/dist/api/internal.d.ts.map +0 -1
  253. package/dist/api/protocol-v2/DeviceSessionOpen.d.ts +0 -28
  254. package/dist/api/protocol-v2/DeviceSessionOpen.d.ts.map +0 -1
  255. package/src/api/internal.ts +0 -9
  256. package/src/api/protocol-v2/DeviceSessionOpen.ts +0 -100
@@ -1,3 +1,6 @@
1
+ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+ import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
3
+
1
4
  import DeviceSettings from '../src/api/device/DeviceSettings';
2
5
 
3
6
  import type { Features } from '../src/types';
@@ -16,19 +19,59 @@ const features = {
16
19
  function createDevice({ protocol }: { protocol: 'V1' | 'V2' }) {
17
20
  const typedCall = jest.fn().mockResolvedValue({ message: { message: 'Success' } });
18
21
  const updateState = jest.fn();
22
+ const getDeviceState = jest.fn();
19
23
  return {
20
24
  device: {
21
25
  features,
22
26
  isProtocolV2: () => protocol === 'V2',
23
27
  commands: { typedCall },
24
28
  updateState,
29
+ getDeviceState,
25
30
  },
26
31
  typedCall,
27
32
  updateState,
33
+ getDeviceState,
28
34
  };
29
35
  }
30
36
 
31
37
  describe('DeviceSettings protocol routing', () => {
38
+ it.each([
39
+ ['label', { label: 'Shared Label' }],
40
+ ['language', { language: 'ja-JP' }],
41
+ ['brightness', { brightness: 80 }],
42
+ ['haptic feedback', { hapticFeedback: true }],
43
+ ])('does not unlock Protocol V2 before changing %s', (_name, settings) => {
44
+ const method = new DeviceSettings({
45
+ id: 1,
46
+ payload: {
47
+ method: 'deviceSettings',
48
+ ...settings,
49
+ },
50
+ });
51
+
52
+ method.init();
53
+
54
+ expect(method.unlockPolicy).toBe('none');
55
+ });
56
+
57
+ it.each([
58
+ ['auto lock', { autoLockDelayMs: 60_000 }],
59
+ ['auto shutdown', { autoShutdownDelayMs: 120_000 }],
60
+ ['mixed settings', { brightness: 80, autoLockDelayMs: 60_000 }],
61
+ ])('unlocks Protocol V2 before changing %s', (_name, settings) => {
62
+ const method = new DeviceSettings({
63
+ id: 1,
64
+ payload: {
65
+ method: 'deviceSettings',
66
+ ...settings,
67
+ },
68
+ });
69
+
70
+ method.init();
71
+
72
+ expect(method.unlockPolicy).toBe('retry-on-locked');
73
+ });
74
+
32
75
  it('uses ApplySettings and commits the confirmed patch for Protocol V1', async () => {
33
76
  const { device, typedCall, updateState } = createDevice({ protocol: 'V1' });
34
77
  const method = new DeviceSettings({
@@ -36,7 +79,7 @@ describe('DeviceSettings protocol routing', () => {
36
79
  payload: {
37
80
  method: 'deviceSettings',
38
81
  label: 'Shared Label',
39
- language: 'ja-JP',
82
+ language: 'ja',
40
83
  },
41
84
  });
42
85
  method.init();
@@ -45,7 +88,7 @@ describe('DeviceSettings protocol routing', () => {
45
88
  await expect(method.run()).resolves.toEqual({ message: 'Success' });
46
89
  expect(typedCall).toHaveBeenCalledWith('ApplySettings', 'Success', {
47
90
  label: 'Shared Label',
48
- language: 'ja-JP',
91
+ language: 'ja',
49
92
  use_passphrase: undefined,
50
93
  homescreen: undefined,
51
94
  _passphrase_source: undefined,
@@ -60,7 +103,7 @@ describe('DeviceSettings protocol routing', () => {
60
103
  expect(updateState).toHaveBeenCalledWith(
61
104
  {
62
105
  identity: { label: 'Shared Label' },
63
- settings: { language: 'ja-JP' },
106
+ settings: { language: 'ja' },
64
107
  },
65
108
  'apply-settings'
66
109
  );
@@ -73,7 +116,7 @@ describe('DeviceSettings protocol routing', () => {
73
116
  payload: {
74
117
  method: 'deviceSettings',
75
118
  label: 'Shared Label',
76
- language: 'ja-JP',
119
+ language: 'ja',
77
120
  autoLockDelayMs: 60_000,
78
121
  hapticFeedback: true,
79
122
  },
@@ -85,7 +128,7 @@ describe('DeviceSettings protocol routing', () => {
85
128
  expect(typedCall).toHaveBeenCalledWith('DeviceSettingsSet', 'Success', {
86
129
  settings: {
87
130
  label: 'Shared Label',
88
- language: 'ja-JP',
131
+ language: 'ja-Jpan-JP',
89
132
  autolock_delay_ms: 60_000,
90
133
  haptic_feedback: true,
91
134
  },
@@ -94,7 +137,7 @@ describe('DeviceSettings protocol routing', () => {
94
137
  {
95
138
  identity: { label: 'Shared Label' },
96
139
  settings: {
97
- language: 'ja-JP',
140
+ language: 'ja-Jpan-JP',
98
141
  autoLockDelayMs: 60_000,
99
142
  hapticFeedback: true,
100
143
  },
@@ -105,4 +148,139 @@ describe('DeviceSettings protocol routing', () => {
105
148
  expect.arrayContaining(['DeviceInfoGet', 'DeviceStatusGet', 'DeviceSettingsGet'])
106
149
  );
107
150
  });
151
+
152
+ it('uses the Pro2 passphrase page as a device-side toggle and verifies the target state', async () => {
153
+ const { device, typedCall, getDeviceState } = createDevice({ protocol: 'V2' });
154
+ getDeviceState
155
+ .mockResolvedValueOnce({
156
+ status: { passphraseProtection: false },
157
+ })
158
+ .mockResolvedValueOnce({
159
+ status: { passphraseProtection: true },
160
+ });
161
+ const method = new DeviceSettings({
162
+ id: 3,
163
+ payload: {
164
+ method: 'deviceSettings',
165
+ usePassphrase: true,
166
+ },
167
+ });
168
+ method.init();
169
+ (method as any).device = device;
170
+
171
+ await expect(method.run()).resolves.toEqual({ message: 'Success' });
172
+ expect(typedCall).toHaveBeenCalledWith('DeviceSettingsPageShow', 'Success', {
173
+ page: DeviceSettingsPage.DevicePassphrase,
174
+ });
175
+ expect(typedCall).not.toHaveBeenCalledWith('DeviceSettingsSet', 'Success', expect.anything());
176
+ expect(getDeviceState).toHaveBeenNthCalledWith(1, { refreshSections: ['status'] });
177
+ expect(getDeviceState).toHaveBeenNthCalledWith(2, { refreshSections: ['status'] });
178
+ });
179
+
180
+ it('does not open a Pro2 settings page when the hardware already matches the target', async () => {
181
+ const { device, typedCall, getDeviceState } = createDevice({ protocol: 'V2' });
182
+ getDeviceState.mockResolvedValue({
183
+ settings: { airgapMode: false },
184
+ });
185
+ const method = new DeviceSettings({
186
+ id: 4,
187
+ payload: {
188
+ method: 'deviceSettings',
189
+ airgapMode: false,
190
+ },
191
+ });
192
+ method.init();
193
+ (method as any).device = device;
194
+
195
+ await expect(method.run()).resolves.toEqual({
196
+ message: 'Settings already match requested value.',
197
+ });
198
+ expect(typedCall).not.toHaveBeenCalled();
199
+ expect(getDeviceState).toHaveBeenCalledWith({ refreshSections: ['settings'] });
200
+ });
201
+
202
+ it('rejects a successful Pro2 page response when the hardware did not reach the target', async () => {
203
+ const { device, getDeviceState } = createDevice({ protocol: 'V2' });
204
+ getDeviceState.mockResolvedValue({
205
+ status: { passphraseProtection: false },
206
+ });
207
+ const method = new DeviceSettings({
208
+ id: 5,
209
+ payload: {
210
+ method: 'deviceSettings',
211
+ usePassphrase: true,
212
+ },
213
+ });
214
+ method.init();
215
+ (method as any).device = device;
216
+
217
+ await expect(method.run()).rejects.toMatchObject({
218
+ errorCode: HardwareErrorCode.RuntimeError,
219
+ message: 'Protocol V2 passphrase setting did not reach the requested value.',
220
+ });
221
+ });
222
+
223
+ it('keeps Protocol V1 passphrase settings on ApplySettings', async () => {
224
+ const { device, typedCall, getDeviceState } = createDevice({ protocol: 'V1' });
225
+ const method = new DeviceSettings({
226
+ id: 6,
227
+ payload: {
228
+ method: 'deviceSettings',
229
+ usePassphrase: true,
230
+ },
231
+ });
232
+ method.init();
233
+ (method as any).device = device;
234
+
235
+ await method.run();
236
+
237
+ expect(typedCall).toHaveBeenCalledWith(
238
+ 'ApplySettings',
239
+ 'Success',
240
+ expect.objectContaining({ use_passphrase: true })
241
+ );
242
+ expect(getDeviceState).not.toHaveBeenCalled();
243
+ });
244
+
245
+ it('rejects combining a Pro2 device-side toggle with direct settings', async () => {
246
+ const { device, typedCall } = createDevice({ protocol: 'V2' });
247
+ const method = new DeviceSettings({
248
+ id: 7,
249
+ payload: {
250
+ method: 'deviceSettings',
251
+ usePassphrase: true,
252
+ label: 'New Label',
253
+ },
254
+ });
255
+ method.init();
256
+ (method as any).device = device;
257
+
258
+ expect(method.unlockPolicy).toBe('none');
259
+ expect(method.protocolV2UiInteraction).toBeUndefined();
260
+ await expect(method.run()).rejects.toMatchObject({
261
+ errorCode: HardwareErrorCode.CallMethodInvalidParameter,
262
+ });
263
+ expect(typedCall).not.toHaveBeenCalled();
264
+ });
265
+
266
+ it('rejects two Pro2 device-side toggles without starting an interaction', async () => {
267
+ const { device, typedCall } = createDevice({ protocol: 'V2' });
268
+ const method = new DeviceSettings({
269
+ id: 8,
270
+ payload: {
271
+ method: 'deviceSettings',
272
+ usePassphrase: true,
273
+ airgapMode: true,
274
+ },
275
+ });
276
+ method.init();
277
+ (method as any).device = device;
278
+
279
+ expect(method.unlockPolicy).toBe('none');
280
+ expect(method.protocolV2UiInteraction).toBeUndefined();
281
+ await expect(method.run()).rejects.toMatchObject({
282
+ errorCode: HardwareErrorCode.CallMethodInvalidParameter,
283
+ });
284
+ expect(typedCall).not.toHaveBeenCalled();
285
+ });
108
286
  });
@@ -23,7 +23,6 @@ describe('Device state events', () => {
23
23
  {
24
24
  protocol: 'V2',
25
25
  identity: { label: 'Renamed', bleName: 'Pro2 1234' },
26
- session: { sessionId: 'private-session', passphraseState: 'private-state' },
27
26
  raw: {
28
27
  protocolV2DeviceInfo: { protocol_version: 2 },
29
28
  },
@@ -31,7 +30,7 @@ describe('Device state events', () => {
31
30
  'apply-settings'
32
31
  );
33
32
 
34
- expect(state?.identity.displayName).toBe('Renamed');
33
+ expect(state?.identity.label).toBe('Renamed');
35
34
  expect(onState).toHaveBeenCalledWith(
36
35
  device,
37
36
  expect.objectContaining({
@@ -35,7 +35,7 @@ describe('DeviceStateMapper', () => {
35
35
  },
36
36
  status: { mode: 'normal', initialized: true, unlocked: true },
37
37
  settings: { language: 'en-US' },
38
- versions: { firmware: '4.10.1', se: '1.1.0.2' },
38
+ versions: { firmware: '4.10.1', se: '1.1.0.2', se01: '1.1.0.2' },
39
39
  });
40
40
  });
41
41
 
@@ -105,18 +105,21 @@ describe('DeviceStateMapper', () => {
105
105
  });
106
106
 
107
107
  test('maps Protocol V2 DeviceInfo without inventing runtime status', () => {
108
- const patch = mapProtocolV2DeviceInfoToState({
109
- protocol_version: 2,
110
- hw: { serial_no: 'SERIAL-1' },
111
- fw: {
112
- application: { version: '5.0.0' },
113
- bootloader: { version: '2.0.0' },
114
- },
115
- coprocessor: {
116
- bt_adv_name: 'Pro2 1234',
117
- application: { version: '1.2.3' },
118
- },
119
- } as ProtocolV2DeviceInfo);
108
+ const patch = mapProtocolV2DeviceInfoToState(
109
+ {
110
+ protocol_version: 2,
111
+ hw: { serial_no: 'SERIAL-1' },
112
+ fw: {
113
+ application: { version: '5.0.0' },
114
+ bootloader: { version: '2.0.0' },
115
+ },
116
+ coprocessor: {
117
+ bt_adv_name: 'Pro2 1234',
118
+ application: { version: '1.2.3' },
119
+ },
120
+ } as ProtocolV2DeviceInfo,
121
+ 'normal'
122
+ );
120
123
 
121
124
  expect(patch.identity).toMatchObject({
122
125
  deviceType: EDeviceType.Pro2,
@@ -133,18 +136,21 @@ describe('DeviceStateMapper', () => {
133
136
  });
134
137
 
135
138
  test('keeps normal mode when Protocol V2 application and SE versions are returned together', () => {
136
- const patch = mapProtocolV2DeviceInfoToState({
137
- protocol_version: 2,
138
- hw: { serial_no: 'SERIAL-NORMAL' },
139
- fw: {
140
- application: { version: '5.0.0' },
141
- bootloader: { version: '2.0.0' },
142
- },
143
- se1: {
144
- application: { version: '1.0.0', build_id: 'se-app', hash: [0x01, 0x02] },
145
- bootloader: { version: '0.1.0', build_id: 'se-boot', hash: [0x03, 0x04] },
146
- },
147
- } as ProtocolV2DeviceInfo);
139
+ const patch = mapProtocolV2DeviceInfoToState(
140
+ {
141
+ protocol_version: 2,
142
+ hw: { serial_no: 'SERIAL-NORMAL' },
143
+ fw: {
144
+ application: { version: '5.0.0' },
145
+ bootloader: { version: '2.0.0' },
146
+ },
147
+ se1: {
148
+ application: { version: '1.0.0', build_id: 'se-app', hash: [0x01, 0x02] },
149
+ bootloader: { version: '0.1.0', build_id: 'se-boot', hash: [0x03, 0x04] },
150
+ },
151
+ } as ProtocolV2DeviceInfo,
152
+ 'normal'
153
+ );
148
154
 
149
155
  expect(patch.status?.mode).toBe('normal');
150
156
  expect(patch.verification).toMatchObject({
@@ -161,6 +167,7 @@ describe('DeviceStateMapper', () => {
161
167
  {
162
168
  hw: { serial_no: 'SERIAL-BOOT' },
163
169
  fw: { bootloader: { version: '2.0.0' } },
170
+ se1: {},
164
171
  },
165
172
  ],
166
173
  [
@@ -174,7 +181,7 @@ describe('DeviceStateMapper', () => {
174
181
  },
175
182
  ],
176
183
  ] as const)('clears stale device identity in Protocol V2 %s mode', (mode, info) => {
177
- const patch = mapProtocolV2DeviceInfoToState(info as ProtocolV2DeviceInfo);
184
+ const patch = mapProtocolV2DeviceInfoToState(info as ProtocolV2DeviceInfo, mode);
178
185
 
179
186
  expect(patch.status?.mode).toBe(mode);
180
187
  expect(patch.identity).toMatchObject({ deviceId: null });
@@ -3,7 +3,7 @@ import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
3
3
  import { DeviceStateStore, createEmptyDeviceState } from '../src/device/DeviceStateStore';
4
4
 
5
5
  describe('DeviceStateStore', () => {
6
- test('merges patches, ignores undefined and recomputes displayName', () => {
6
+ test('merges identity patches and ignores undefined', () => {
7
7
  const store = new DeviceStateStore(
8
8
  createEmptyDeviceState({
9
9
  deviceType: EDeviceType.Pro2,
@@ -20,11 +20,9 @@ describe('DeviceStateStore', () => {
20
20
  expect(result.state.identity).toMatchObject({
21
21
  label: 'My Wallet',
22
22
  bleName: 'Pro2 1234',
23
- displayName: 'My Wallet',
24
23
  });
25
24
  expect(result.revision).toBe(2);
26
25
  expect(result.changedKeys).toContain('identity.label');
27
- expect(result.changedKeys).toContain('identity.displayName');
28
26
  });
29
27
 
30
28
  test('does not advance revision when the patch has no real changes', () => {
@@ -47,22 +45,6 @@ describe('DeviceStateStore', () => {
47
45
  expect(result.changedKeys).toContain('protocolVersion');
48
46
  });
49
47
 
50
- test('clears only ephemeral session state', () => {
51
- const store = new DeviceStateStore(createEmptyDeviceState());
52
- store.update(
53
- {
54
- identity: { label: 'Kept' },
55
- session: { sessionId: 'session-1', passphraseState: 'state-1' },
56
- },
57
- 'initialize'
58
- );
59
-
60
- const result = store.clearSession('transport-reconnect');
61
-
62
- expect(result?.state.identity.label).toBe('Kept');
63
- expect(result?.state.session).toBeUndefined();
64
- });
65
-
66
48
  test('merges raw protocol sources instead of replacing the previous source', () => {
67
49
  const store = new DeviceStateStore(createEmptyDeviceState());
68
50
  const deviceInfo = { protocol_version: 2, hw: { serial_no: 'SERIAL-1' } };
@@ -97,19 +79,4 @@ describe('DeviceStateStore', () => {
97
79
  protocolV2DeviceInfo: deviceInfo,
98
80
  });
99
81
  });
100
-
101
- test.each([
102
- [EDeviceType.Classic1s, 'OneKey Classic 1S'],
103
- [EDeviceType.ClassicPure, 'OneKey Classic 1S Pure'],
104
- [EDeviceType.Touch, 'OneKey Touch'],
105
- [EDeviceType.Pro, 'OneKey Pro'],
106
- [EDeviceType.Pro2, 'OneKey Pro 2'],
107
- ] as const)(
108
- 'uses the product display name for %s when label and BLE name are absent',
109
- (type, name) => {
110
- const state = createEmptyDeviceState({ deviceType: type });
111
-
112
- expect(state.identity.displayName).toBe(name);
113
- }
114
- );
115
82
  });
@@ -0,0 +1,106 @@
1
+ import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
+
3
+ import {
4
+ getDeviceSerialNo,
5
+ getDeviceType,
6
+ getDeviceUUID,
7
+ getFirmwareType,
8
+ } from '../src/utils/deviceInfoUtils';
9
+ import {
10
+ getDeviceBLEFirmwareVersion,
11
+ getDeviceBoardloaderVersion,
12
+ getDeviceBootloaderVersion,
13
+ getDeviceFirmwareVersion,
14
+ } from '../src/utils/deviceVersionUtils';
15
+ import {
16
+ buildProtocolV1FeaturesPayload,
17
+ buildProtocolV2FeaturesPayload,
18
+ } from '../src/deviceProfile';
19
+
20
+ import type { PROTO } from '../src/constants';
21
+
22
+ describe('device feature selectors', () => {
23
+ test('reads legacy Protocol V1 features without exposing the internal builder', () => {
24
+ const features = {
25
+ onekey_device_type: 'CLASSIC1S',
26
+ onekey_serial_no: 'CP123456',
27
+ fw_vendor: 'OneKey Bitcoin-only',
28
+ onekey_firmware_version: 'v3.5.0',
29
+ onekey_boot_version: '2.8.0-alpha.1',
30
+ onekey_board_version: '1.2.3+board',
31
+ onekey_ble_version: '1.4.0+ble',
32
+ } as PROTO.Features;
33
+
34
+ expect(getDeviceType(features)).toBe(EDeviceType.Classic1s);
35
+ expect(getDeviceSerialNo(features)).toBe('CP123456');
36
+ expect(getFirmwareType(features)).toBe(EFirmwareType.BitcoinOnly);
37
+ expect(getDeviceFirmwareVersion(features)).toEqual([3, 5, 0]);
38
+ expect(getDeviceBootloaderVersion(features)).toEqual([2, 8, 0]);
39
+ expect(getDeviceBoardloaderVersion(features)).toEqual([1, 2, 3]);
40
+ expect(getDeviceBLEFirmwareVersion(features)).toEqual([1, 4, 0]);
41
+ });
42
+
43
+ test('keeps getDeviceUUID as a compatibility alias', () => {
44
+ const features = {
45
+ onekey_serial_no: 'PR123456',
46
+ fw_vendor: 'OneKey Bitcoin-only',
47
+ onekey_board_version: '1.2.3',
48
+ onekey_ble_version: '2.3.4',
49
+ } as PROTO.Features;
50
+
51
+ expect(getDeviceUUID(features)).toBe(getDeviceSerialNo(features));
52
+ });
53
+
54
+ test('uses bootloader version parts from old Protocol V1 firmware', () => {
55
+ const features = {
56
+ bootloader_mode: true,
57
+ major_version: 1,
58
+ minor_version: 8,
59
+ patch_version: 3,
60
+ model: '1',
61
+ } as PROTO.Features;
62
+
63
+ expect(getDeviceType(features)).toBe(EDeviceType.Classic);
64
+ expect(getDeviceBootloaderVersion(features)).toEqual([1, 8, 3]);
65
+ });
66
+
67
+ test('shares Protocol V1 compatibility semantics with the internal builder', () => {
68
+ const rawFeatures = {
69
+ onekey_device_type: 'PRO',
70
+ onekey_serial_no: 'PR123456',
71
+ onekey_firmware_version: '4.16.0',
72
+ onekey_boot_version: '2.8.0',
73
+ fw_vendor: 'OneKey Bitcoin-only',
74
+ } as PROTO.Features;
75
+ const normalized = buildProtocolV1FeaturesPayload(rawFeatures);
76
+
77
+ expect(getDeviceType(normalized)).toBe(getDeviceType(rawFeatures));
78
+ expect(getDeviceSerialNo(normalized)).toBe(getDeviceSerialNo(rawFeatures));
79
+ expect(getFirmwareType(normalized)).toBe(getFirmwareType(rawFeatures));
80
+ expect(getDeviceFirmwareVersion(normalized)).toEqual(getDeviceFirmwareVersion(rawFeatures));
81
+ expect(getDeviceBootloaderVersion(normalized)).toEqual(getDeviceBootloaderVersion(rawFeatures));
82
+ });
83
+
84
+ test('reads normalized Protocol V2 features through the same public selectors', () => {
85
+ const features = buildProtocolV2FeaturesPayload({
86
+ deviceInfo: {
87
+ hw: { serial_no: 'P2-001' },
88
+ fw: {
89
+ application: { version: '5.0.0' },
90
+ bootloader: { version: '2.0.0' },
91
+ romloader: { version: '1.0.0' },
92
+ },
93
+ coprocessor: {
94
+ application: { version: '3.0.0' },
95
+ },
96
+ },
97
+ });
98
+
99
+ expect(getDeviceType(features)).toBe(EDeviceType.Pro2);
100
+ expect(getDeviceSerialNo(features)).toBe('P2-001');
101
+ expect(getDeviceFirmwareVersion(features)).toEqual([5, 0, 0]);
102
+ expect(getDeviceBootloaderVersion(features)).toEqual([2, 0, 0]);
103
+ expect(getDeviceBoardloaderVersion(features)).toEqual([1, 0, 0]);
104
+ expect(getDeviceBLEFirmwareVersion(features)).toEqual([3, 0, 0]);
105
+ });
106
+ });
@@ -4,7 +4,6 @@ import {
4
4
  deviceWalletSessionStore,
5
5
  } from '../src/device/DeviceWalletSessionStore';
6
6
  import { Device } from '../src/device/Device';
7
- import { DevicePool } from '../src/device/DevicePool';
8
7
  import { createCoreApi } from '../src/inject';
9
8
 
10
9
  jest.mock('../src/data/config', () => ({
@@ -45,7 +44,10 @@ describe('DeviceWalletSessionStore', () => {
45
44
  store.set('ble-path', 'hidden-a', 'session-a');
46
45
  store.setPending('ble-path', 'pending-session');
47
46
 
48
- store.migrateDeviceKey('ble-path', 'stable-device-id');
47
+ store.reconcileDeviceIdentity({
48
+ temporaryKey: 'ble-path',
49
+ nextDeviceId: 'stable-device-id',
50
+ });
49
51
 
50
52
  expect(store.get('ble-path', 'hidden-a')).toBeUndefined();
51
53
  expect(store.get('stable-device-id', 'hidden-a')).toBe('session-a');
@@ -53,6 +55,25 @@ describe('DeviceWalletSessionStore', () => {
53
55
  expect(store.getPending('stable-device-id')).toBe('pending-session');
54
56
  });
55
57
 
58
+ test('drops sessions from an old stable identity without overwriting the new device', () => {
59
+ const store = new DeviceWalletSessionStore();
60
+ store.set('device-a', 'hidden-a', 'session-a');
61
+ store.setPending('device-a', 'pending-a');
62
+ store.set('device-b', 'hidden-a', 'session-b');
63
+ store.setPending('device-b', 'pending-b');
64
+
65
+ store.reconcileDeviceIdentity({
66
+ temporaryKey: 'ble-path',
67
+ previousDeviceId: 'device-a',
68
+ nextDeviceId: 'device-b',
69
+ });
70
+
71
+ expect(store.get('device-a', 'hidden-a')).toBeUndefined();
72
+ expect(store.getPending('device-a')).toBeUndefined();
73
+ expect(store.get('device-b', 'hidden-a')).toBe('session-b');
74
+ expect(store.getPending('device-b')).toBe('pending-b');
75
+ });
76
+
56
77
  test('clears one wallet, one device, or all sessions', () => {
57
78
  const store = new DeviceWalletSessionStore();
58
79
  store.set('device-1', 'hidden-a', 'session-a');
@@ -75,34 +96,48 @@ describe('DeviceWalletSessionStore', () => {
75
96
  describe('ClearSessionCache', () => {
76
97
  beforeEach(() => {
77
98
  deviceWalletSessionStore.clear();
78
- DevicePool.devicesCache = {};
79
99
  });
80
100
 
81
- afterEach(() => {
82
- DevicePool.devicesCache = {};
83
- });
101
+ test('uses DeviceWalletSessionStore as the only cache source', async () => {
102
+ deviceWalletSessionStore.set('device-1', 'hidden-a', 'session-a');
103
+ deviceWalletSessionStore.set('device-2', 'hidden-a', 'session-b');
84
104
 
85
- test('clears matching sessions from both runtime stores', async () => {
86
- const matchingDevice = Device.fromDescriptor({ id: 'one', path: 'one' } as never);
87
- matchingDevice.updateState(
88
- {
89
- protocol: 'V1',
90
- identity: { deviceId: 'device-1' },
91
- session: { sessionId: 'session-a', passphraseState: 'hidden-a' },
105
+ const method = new ClearSessionCache({
106
+ payload: {
107
+ method: 'clearSessionCache',
108
+ deviceId: 'device-1',
109
+ passphraseState: 'hidden-a',
92
110
  },
93
- 'initialize'
94
- );
95
- const otherDevice = Device.fromDescriptor({ id: 'two', path: 'two' } as never);
96
- otherDevice.updateState(
97
- {
98
- protocol: 'V1',
99
- identity: { deviceId: 'device-2' },
100
- session: { sessionId: 'session-b', passphraseState: 'hidden-a' },
111
+ });
112
+ method.init();
113
+ await method.run();
114
+
115
+ expect(deviceWalletSessionStore.get('device-1', 'hidden-a')).toBeUndefined();
116
+ expect(deviceWalletSessionStore.get('device-2', 'hidden-a')).toBe('session-b');
117
+ });
118
+
119
+ test('keeps legacy Features session fields as projections of the store', async () => {
120
+ const device = Device.fromDescriptor({ id: 'one', path: 'one' } as never);
121
+ device.features = {
122
+ protocol: 'V1',
123
+ deviceId: 'device-1',
124
+ passphraseState: 'hidden-a',
125
+ sessionId: 'session-a',
126
+ session_id: 'session-a',
127
+ raw: {
128
+ protocolV1Features: {
129
+ sessionId: 'session-a',
130
+ session_id: 'session-a',
131
+ },
101
132
  },
102
- 'initialize'
103
- );
104
- DevicePool.devicesCache = { one: matchingDevice, two: otherDevice };
105
- deviceWalletSessionStore.set('device-1', 'hidden-a', 'session-a');
133
+ } as never;
134
+
135
+ expect(device.state).not.toHaveProperty('session');
136
+ expect(device.features).toMatchObject({
137
+ sessionId: 'session-a',
138
+ session_id: 'session-a',
139
+ passphraseState: 'hidden-a',
140
+ });
106
141
 
107
142
  const method = new ClearSessionCache({
108
143
  payload: {
@@ -114,10 +149,11 @@ describe('ClearSessionCache', () => {
114
149
  method.init();
115
150
  await method.run();
116
151
 
117
- expect((matchingDevice.state as any)?.session).toBeUndefined();
118
- expect((otherDevice.state as any)?.session).toEqual({
119
- sessionId: 'session-b',
120
- passphraseState: 'hidden-a',
152
+ expect(device.features?.sessionId).toBeNull();
153
+ expect(device.features?.session_id).toBeNull();
154
+ expect(device.state?.raw?.protocolV1Features).toMatchObject({
155
+ sessionId: 'session-a',
156
+ session_id: 'session-a',
121
157
  });
122
158
  });
123
159