@onekeyfe/hd-core 1.2.0-alpha.52 → 1.2.0-alpha.53

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 (251) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +78 -0
  2. package/__tests__/DeviceCommands.test.ts +45 -0
  3. package/__tests__/DeviceEventRegistration.test.ts +6 -0
  4. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +198 -3
  5. package/__tests__/connectSettings.test.ts +5 -47
  6. package/__tests__/device-lifecycle-events.test.ts +105 -3
  7. package/__tests__/deviceUploadNft.test.ts +293 -0
  8. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
  9. package/__tests__/get-device-state.test.ts +132 -12
  10. package/__tests__/homescreen.test.ts +41 -1
  11. package/__tests__/logBlockEvent.test.ts +1 -1
  12. package/__tests__/method-protocol-support.test.ts +19 -0
  13. package/__tests__/open-wallet-session.test.ts +446 -33
  14. package/__tests__/pro2Nft.test.ts +108 -0
  15. package/__tests__/protocol-binding.test.ts +89 -0
  16. package/__tests__/protocol-v2-resources.test.ts +284 -0
  17. package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
  18. package/__tests__/protocol-v2-unlock-policy.test.ts +201 -35
  19. package/__tests__/protocol-v2.test.ts +1349 -1005
  20. package/__tests__/protocolV2UiInteraction.test.ts +63 -0
  21. package/__tests__/public-pro2-api-boundary.test.ts +1 -0
  22. package/__tests__/search-devices.test.ts +12 -3
  23. package/__tests__/uiProgressThrottle.test.ts +74 -0
  24. package/__tests__/uiPromiseRegistry.test.ts +86 -0
  25. package/dist/api/BaseMethod.d.ts +3 -1
  26. package/dist/api/BaseMethod.d.ts.map +1 -1
  27. package/dist/api/CheckAllFirmwareRelease.d.ts +2 -1
  28. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  29. package/dist/api/DetectDeviceConnectProtocol.d.ts +7 -0
  30. package/dist/api/DetectDeviceConnectProtocol.d.ts.map +1 -0
  31. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  32. package/dist/api/FirmwareUpdateV2.d.ts +0 -11
  33. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  34. package/dist/api/FirmwareUpdateV3.d.ts +3 -7
  35. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  36. package/dist/api/FirmwareUpdateV4.d.ts +11 -25
  37. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  38. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  39. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  40. package/dist/api/SearchDevices.d.ts.map +1 -1
  41. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  42. package/dist/api/device/DeviceUnlock.d.ts +2 -2
  43. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  44. package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
  45. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  46. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -9
  47. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  48. package/dist/api/firmware/getBinary.d.ts +0 -7
  49. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  50. package/dist/api/firmware/updateBootloader.d.ts +0 -2
  51. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  52. package/dist/api/firmware/uploadFirmware.d.ts +1 -9
  53. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  54. package/dist/api/helpers/protocolV2FileWrite.d.ts +2 -0
  55. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  56. package/dist/api/index.d.ts +2 -0
  57. package/dist/api/index.d.ts.map +1 -1
  58. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
  59. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
  60. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  61. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  62. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  63. package/dist/core/deviceEventRegistration.d.ts +2 -0
  64. package/dist/core/deviceEventRegistration.d.ts.map +1 -1
  65. package/dist/core/index.d.ts.map +1 -1
  66. package/dist/core/uiPromiseRegistry.d.ts +4 -0
  67. package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
  68. package/dist/data-manager/DataManager.d.ts +4 -2
  69. package/dist/data-manager/DataManager.d.ts.map +1 -1
  70. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  71. package/dist/device/Device.d.ts +29 -6
  72. package/dist/device/Device.d.ts.map +1 -1
  73. package/dist/device/DeviceCommands.d.ts +4 -4
  74. package/dist/device/DeviceCommands.d.ts.map +1 -1
  75. package/dist/device/DevicePool.d.ts.map +1 -1
  76. package/dist/events/device.d.ts +4 -0
  77. package/dist/events/device.d.ts.map +1 -1
  78. package/dist/events/logBlockEvent.d.ts.map +1 -1
  79. package/dist/events/ui-promise.d.ts +4 -2
  80. package/dist/events/ui-promise.d.ts.map +1 -1
  81. package/dist/events/ui-request.d.ts +31 -2
  82. package/dist/events/ui-request.d.ts.map +1 -1
  83. package/dist/events/ui-response.d.ts +10 -2
  84. package/dist/events/ui-response.d.ts.map +1 -1
  85. package/dist/index.d.ts +214 -231
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +2559 -3497
  88. package/dist/inject.d.ts +6 -1
  89. package/dist/inject.d.ts.map +1 -1
  90. package/dist/lowLevelInject.d.ts.map +1 -1
  91. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  92. package/dist/protocols/protocol-v2/resources.d.ts +30 -0
  93. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
  94. package/dist/protocols/protocol-v2/uiInteraction.d.ts +7 -4
  95. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  96. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
  97. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
  98. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
  99. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
  100. package/dist/protocols/protocol-v2/walletSession.d.ts +5 -1
  101. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  102. package/dist/topLevelInject.d.ts.map +1 -1
  103. package/dist/types/api/checkAllFirmwareRelease.d.ts +2 -4
  104. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  105. package/dist/types/api/detectDeviceConnectProtocol.d.ts +4 -0
  106. package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +1 -0
  107. package/dist/types/api/deviceUnlock.d.ts +5 -1
  108. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  109. package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
  110. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  111. package/dist/types/api/export.d.ts +2 -4
  112. package/dist/types/api/export.d.ts.map +1 -1
  113. package/dist/types/api/firmwareUpdate.d.ts +2 -70
  114. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  115. package/dist/types/api/index.d.ts +7 -10
  116. package/dist/types/api/index.d.ts.map +1 -1
  117. package/dist/types/api/protocolV2.d.ts +3 -0
  118. package/dist/types/api/protocolV2.d.ts.map +1 -1
  119. package/dist/types/device.d.ts.map +1 -1
  120. package/dist/types/params.d.ts +1 -0
  121. package/dist/types/params.d.ts.map +1 -1
  122. package/dist/types/settings.d.ts +22 -13
  123. package/dist/types/settings.d.ts.map +1 -1
  124. package/dist/utils/deviceFeaturesCompat.d.ts +0 -1
  125. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  126. package/dist/utils/deviceFeaturesUtils.d.ts +4 -0
  127. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  128. package/dist/utils/homescreen.d.ts +4 -0
  129. package/dist/utils/homescreen.d.ts.map +1 -1
  130. package/dist/utils/index.d.ts +1 -1
  131. package/dist/utils/index.d.ts.map +1 -1
  132. package/dist/utils/patch.d.ts +1 -1
  133. package/dist/utils/patch.d.ts.map +1 -1
  134. package/dist/utils/pro2Nft.d.ts +31 -0
  135. package/dist/utils/pro2Nft.d.ts.map +1 -0
  136. package/dist/utils/pro2Wallpaper.d.ts +10 -0
  137. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  138. package/dist/utils/uiProgressThrottle.d.ts +3 -0
  139. package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
  140. package/package.json +4 -4
  141. package/src/api/BaseMethod.ts +8 -10
  142. package/src/api/CheckAllFirmwareRelease.ts +54 -57
  143. package/src/api/DetectDeviceConnectProtocol.ts +18 -0
  144. package/src/api/FirmwareUpdate.ts +7 -15
  145. package/src/api/FirmwareUpdateV2.ts +126 -316
  146. package/src/api/FirmwareUpdateV3.ts +63 -265
  147. package/src/api/FirmwareUpdateV4.ts +566 -965
  148. package/src/api/GetPassphraseState.ts +7 -1
  149. package/src/api/OpenWalletSession.ts +54 -17
  150. package/src/api/SearchDevices.ts +3 -1
  151. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +32 -11
  152. package/src/api/device/DeviceUnlock.ts +8 -3
  153. package/src/api/device/DeviceUpdateBootloader.ts +6 -122
  154. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +16 -83
  155. package/src/api/firmware/updateBootloader.ts +4 -19
  156. package/src/api/firmware/uploadFirmware.ts +39 -144
  157. package/src/api/helpers/protocolV2FileWrite.ts +11 -4
  158. package/src/api/index.ts +2 -0
  159. package/src/api/protocol-v2/DeviceUploadNft.ts +189 -0
  160. package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
  161. package/src/api/protocol-v2/helpers.ts +1 -0
  162. package/src/core/deviceEventRegistration.ts +4 -0
  163. package/src/core/index.ts +167 -83
  164. package/src/core/uiPromiseRegistry.ts +41 -0
  165. package/src/data/messages/messages-protocol-v2.json +25 -0
  166. package/src/data-manager/DataManager.ts +67 -38
  167. package/src/data-manager/connectSettings.ts +0 -11
  168. package/src/device/Device.ts +258 -42
  169. package/src/device/DeviceCommands.ts +7 -1
  170. package/src/device/DevicePool.ts +7 -2
  171. package/src/events/device.ts +4 -0
  172. package/src/events/logBlockEvent.ts +1 -0
  173. package/src/events/ui-promise.ts +4 -2
  174. package/src/events/ui-request.ts +36 -2
  175. package/src/events/ui-response.ts +12 -2
  176. package/src/index.ts +0 -5
  177. package/src/inject.ts +60 -24
  178. package/src/lowLevelInject.ts +7 -8
  179. package/src/protocols/protocol-v2/features.ts +10 -3
  180. package/src/protocols/protocol-v2/resources.ts +359 -0
  181. package/src/protocols/protocol-v2/uiInteraction.ts +46 -6
  182. package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
  183. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +118 -0
  184. package/src/protocols/protocol-v2/walletSession.ts +180 -30
  185. package/src/topLevelInject.ts +7 -8
  186. package/src/types/api/checkAllFirmwareRelease.ts +2 -4
  187. package/src/types/api/detectDeviceConnectProtocol.ts +7 -0
  188. package/src/types/api/deviceUnlock.ts +12 -1
  189. package/src/types/api/deviceUpdateBootloader.ts +0 -6
  190. package/src/types/api/export.ts +1 -18
  191. package/src/types/api/firmwareUpdate.ts +3 -76
  192. package/src/types/api/index.ts +13 -14
  193. package/src/types/api/protocolV2.ts +13 -0
  194. package/src/types/device.ts +3 -0
  195. package/src/types/params.ts +7 -1
  196. package/src/types/settings.ts +42 -13
  197. package/src/utils/deviceFeaturesCompat.ts +0 -6
  198. package/src/utils/deviceFeaturesUtils.ts +8 -0
  199. package/src/utils/homescreen.ts +32 -6
  200. package/src/utils/index.ts +6 -1
  201. package/src/utils/pro2Nft.ts +142 -0
  202. package/src/utils/pro2Wallpaper.ts +35 -8
  203. package/src/utils/uiProgressThrottle.ts +63 -0
  204. package/__tests__/device-initialize-timeout.test.ts +0 -56
  205. package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -175
  206. package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
  207. package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
  208. package/__tests__/firmware-update/firmware-preparation-error.test.ts +0 -27
  209. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +0 -200
  210. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
  211. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +0 -294
  212. package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
  213. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -213
  214. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +0 -58
  215. package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
  216. package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
  217. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
  218. package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
  219. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
  220. package/dist/api/firmware/FirmwarePreparationError.d.ts +0 -3
  221. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +0 -1
  222. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
  223. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
  224. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -25
  225. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
  226. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -32
  227. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
  228. package/dist/api/firmware/progressThrottle.d.ts +0 -3
  229. package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
  230. package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
  231. package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
  232. package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
  233. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
  234. package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
  235. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
  236. package/dist/types/api/firmwareUpdatePlan.d.ts +0 -28
  237. package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
  238. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
  239. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
  240. package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
  241. package/src/api/firmware/FirmwareHostBinding.ts +0 -100
  242. package/src/api/firmware/FirmwarePreparationError.ts +0 -12
  243. package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
  244. package/src/api/firmware/FirmwareUpdatePlan.ts +0 -772
  245. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -415
  246. package/src/api/firmware/progressThrottle.ts +0 -44
  247. package/src/protocols/protocol-v2/lockedError.ts +0 -10
  248. package/src/protocols/protocol-v2/unlockRetry.ts +0 -109
  249. package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
  250. package/src/types/api/firmwareUpdatePlan.ts +0 -38
  251. package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
@@ -18,6 +18,83 @@ class TestAllNetworkMethod extends AllNetworkGetAddressBase {
18
18
  }
19
19
 
20
20
  describe('AllNetworkGetAddressBase tracing', () => {
21
+ test('resumes a Protocol V2 hidden wallet before running a nested chain method', async () => {
22
+ const calls: string[] = [];
23
+ const checkPassphraseStateSafety = jest.fn().mockImplementation(() => {
24
+ calls.push('resume-hidden-session');
25
+ return Promise.resolve(true);
26
+ });
27
+ const innerMethod = {
28
+ checkSafetyLevelOnTestNet: jest.fn().mockResolvedValue(false),
29
+ connectId: 'connect-id',
30
+ deviceId: 'device-id',
31
+ getVersionRange: jest.fn().mockReturnValue({}),
32
+ assertProtocolSupported: jest.fn(),
33
+ init: jest.fn(),
34
+ name: 'evmGetAddress',
35
+ responseID: 43,
36
+ unlockPolicy: 'unlock-before-run',
37
+ run: jest.fn().mockImplementation(() => {
38
+ calls.push('run-chain-method');
39
+ return Promise.resolve([{ address: '0xhidden' }]);
40
+ }),
41
+ setDevice: jest.fn(),
42
+ strictCheckDeviceSupport: false,
43
+ };
44
+ (findMethod as jest.Mock).mockReturnValue(innerMethod);
45
+ const method = new TestAllNetworkMethod({
46
+ id: 1,
47
+ payload: {
48
+ method: 'allNetworkGetAddress',
49
+ connectId: 'connect-id',
50
+ deviceId: 'device-id',
51
+ passphraseState: 'hidden-state',
52
+ bundle: [],
53
+ },
54
+ });
55
+ method.protocolV2UnlockContext = { preflightCompleted: true };
56
+ const typedCall = jest.fn();
57
+ method.device = {
58
+ checkPassphraseStateSafety,
59
+ commands: {
60
+ typedCall,
61
+ },
62
+ getCurrentFirmwareType: jest.fn(),
63
+ getProtocol: jest.fn().mockReturnValue('V2'),
64
+ getCurrentFirmwareVersionString: jest.fn().mockReturnValue('1.0.0'),
65
+ getCurrentMethodVersionRange: jest
66
+ .fn()
67
+ .mockImplementation((getRange: (type: string) => unknown) => getRange('pro2')),
68
+ instanceId: 'device-instance',
69
+ isProtocolV2: jest.fn().mockReturnValue(true),
70
+ isBootloader: jest.fn().mockReturnValue(false),
71
+ isRomloader: jest.fn().mockReturnValue(false),
72
+ off: jest.fn(),
73
+ on: jest.fn(),
74
+ state: { status: { unlocked: true } },
75
+ updateProtocolV2Status: jest.fn(),
76
+ } as any;
77
+
78
+ await method.callMethod(
79
+ 'evmGetAddress',
80
+ {
81
+ bundle: [
82
+ {
83
+ _originRequestParams: {
84
+ network: 'evm',
85
+ path: "m/44'/60'/0'/0/0",
86
+ },
87
+ },
88
+ ],
89
+ },
90
+ 0
91
+ );
92
+
93
+ expect(checkPassphraseStateSafety).toHaveBeenCalledWith('hidden-state', false, undefined);
94
+ expect(calls).toEqual(['resume-hidden-session', 'run-chain-method']);
95
+ expect(typedCall).not.toHaveBeenCalled();
96
+ });
97
+
21
98
  test('releases the nested request context when an unhandled error escapes', async () => {
22
99
  const deviceInstanceId = 'device-instance';
23
100
  const innerMethod = {
@@ -51,6 +128,7 @@ describe('AllNetworkGetAddressBase tracing', () => {
51
128
  getCurrentMethodVersionRange: jest
52
129
  .fn()
53
130
  .mockImplementation((getRange: (type: string) => unknown) => getRange('classic')),
131
+ isProtocolV2: jest.fn().mockReturnValue(false),
54
132
  off: jest.fn(),
55
133
  on: jest.fn(),
56
134
  } as any;
@@ -125,6 +125,51 @@ describe('DeviceCommands failure mapping', () => {
125
125
  expect(JSON.stringify(log.messages)).not.toContain('secret-wallet-address');
126
126
  });
127
127
 
128
+ it('logs the sanitized DeviceFirmwareUpdateStatus response payload', async () => {
129
+ const commands = createCommands();
130
+ const log = getLogger(LoggerNames.DeviceCommands);
131
+ log.messages.length = 0;
132
+
133
+ await expect(
134
+ commands._filterCommonTypes(
135
+ {
136
+ type: 'DeviceFirmwareUpdateStatus',
137
+ message: {
138
+ records: [
139
+ {
140
+ target_id: 4,
141
+ status: 2,
142
+ payload_version: 0x010203,
143
+ path: 'vol0:/application_p1.bin',
144
+ },
145
+ ],
146
+ },
147
+ } as any,
148
+ 'DeviceFirmwareUpdateStatusGet'
149
+ )
150
+ ).resolves.toMatchObject({ type: 'DeviceFirmwareUpdateStatus' });
151
+
152
+ expect(log.messages.at(-1)?.message).toEqual([
153
+ '_filterCommonTypes: ',
154
+ {
155
+ request: 'DeviceFirmwareUpdateStatusGet',
156
+ response: {
157
+ type: 'DeviceFirmwareUpdateStatus',
158
+ message: {
159
+ records: [
160
+ {
161
+ target_id: 4,
162
+ status: 2,
163
+ payload_version: 0x010203,
164
+ path: 'vol0:/application_p1.bin',
165
+ },
166
+ ],
167
+ },
168
+ },
169
+ },
170
+ ]);
171
+ });
172
+
128
173
  it('maps an invalid DeviceSessionGet resume to WalletSessionInvalid', async () => {
129
174
  const commands = createCommands();
130
175
 
@@ -3,6 +3,8 @@ import { registerHardwareUiEventListeners } from '../src/core/deviceEventRegistr
3
3
 
4
4
  const handlers = {
5
5
  pin: jest.fn(),
6
+ pinOnDevice: jest.fn(),
7
+ pinOnDeviceComplete: jest.fn(),
6
8
  button: jest.fn(),
7
9
  passphrase: jest.fn(),
8
10
  passphraseOnDevice: jest.fn(),
@@ -21,6 +23,8 @@ describe('hardware UI event registration', () => {
21
23
  expect(registerHardwareUiEventListeners(device as any, handlers)).toBe(true);
22
24
  expect(device.on.mock.calls.map(([type]) => type)).toEqual([
23
25
  DEVICE.PIN,
26
+ DEVICE.PIN_ON_DEVICE,
27
+ DEVICE.PIN_ON_DEVICE_COMPLETE,
24
28
  DEVICE.BUTTON,
25
29
  DEVICE.PASSPHRASE,
26
30
  DEVICE.PASSPHRASE_ON_DEVICE,
@@ -34,6 +38,8 @@ describe('hardware UI event registration', () => {
34
38
  expect(registerHardwareUiEventListeners(device as any, handlers)).toBe(true);
35
39
  expect(device.on.mock.calls.map(([type]) => type)).toEqual([
36
40
  DEVICE.PIN,
41
+ DEVICE.PIN_ON_DEVICE,
42
+ DEVICE.PIN_ON_DEVICE_COMPLETE,
37
43
  DEVICE.BUTTON,
38
44
  DEVICE.PASSPHRASE,
39
45
  DEVICE.PASSPHRASE_ON_DEVICE,
@@ -5,6 +5,7 @@ import CheckAllFirmwareRelease, {
5
5
  } from '../src/api/CheckAllFirmwareRelease';
6
6
  import { DataManager } from '../src/data-manager';
7
7
  import { createCoreApi } from '../src/inject';
8
+ import { PROTOCOL_V2_RESOURCE_DEVICE_PATHS } from '../src/protocols/protocol-v2/resources';
8
9
 
9
10
  import type { CoreApi } from '../src/types/api';
10
11
  import type { DeviceStateVersions, IFirmwareReleaseInfo } from '../src/types';
@@ -72,7 +73,74 @@ const release: IFirmwareReleaseInfo = {
72
73
  ],
73
74
  };
74
75
 
76
+ const stableResources = ['images', 'animation', 'wallpaper', 'translations', 'roobert', 'noto'].map(
77
+ (type, index) => ({
78
+ type,
79
+ url: `https://example.com/${type}.okpkg`,
80
+ size: 0x52a0 + index + 1,
81
+ fileHash: 'a'.repeat(64),
82
+ headerHash: index.toString(16).padStart(128, '0'),
83
+ })
84
+ );
85
+
86
+ const createFilesystemTypedCall = (missingAll = false) => {
87
+ const resourceByPath = new Map(
88
+ stableResources.map(resource => [
89
+ PROTOCOL_V2_RESOURCE_DEVICE_PATHS[
90
+ resource.type as keyof typeof PROTOCOL_V2_RESOURCE_DEVICE_PATHS
91
+ ],
92
+ resource,
93
+ ])
94
+ );
95
+ return jest.fn((requestType: string, _responseType: string, payload: Record<string, any>) => {
96
+ if (requestType === 'ResourceInventoryGet') {
97
+ throw new Error('ResourceInventoryGet is unavailable on released firmware');
98
+ }
99
+ if (requestType === 'FilesystemPathInfoQuery') {
100
+ const resource = resourceByPath.get(payload.path);
101
+ return {
102
+ message: {
103
+ exist: Boolean(resource) && !missingAll,
104
+ directory: false,
105
+ size: missingAll ? 0 : resource?.size,
106
+ },
107
+ };
108
+ }
109
+ if (requestType === 'FilesystemFileRead') {
110
+ const resource = resourceByPath.get(payload.file.path);
111
+ if (!resource || missingAll) throw new Error('missing resource');
112
+ const header = new Uint8Array(0x52a0);
113
+ const view = new DataView(header.buffer);
114
+ 'OKPP'.split('').forEach((char, index) => {
115
+ header[index] = char.charCodeAt(0);
116
+ });
117
+ 'RESC'.split('').forEach((char, index) => {
118
+ header[0x08 + index] = char.charCodeAt(0);
119
+ });
120
+ view.setUint32(0x0c, header.byteLength, true);
121
+ for (let index = 0; index < resource.headerHash.length / 2; index++) {
122
+ header[0x240 + index] = Number.parseInt(
123
+ resource.headerHash.slice(index * 2, index * 2 + 2),
124
+ 16
125
+ );
126
+ }
127
+ const offset = Number(payload.file.offset);
128
+ const chunkLength = Number(payload.chunk_len);
129
+ return {
130
+ message: {
131
+ data: header.slice(offset, offset + chunkLength),
132
+ },
133
+ };
134
+ }
135
+ throw new Error(`Unexpected request: ${requestType}`);
136
+ });
137
+ };
138
+
75
139
  describe('checkAllFirmwareRelease Protocol V2 support', () => {
140
+ afterEach(() => {
141
+ jest.restoreAllMocks();
142
+ });
143
+
76
144
  test('builds ordered firmwareUpdateV4 targets from recommended component versions', () => {
77
145
  const result = buildProtocolV2FirmwareRelease({
78
146
  currentVersions,
@@ -84,7 +152,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
84
152
  status: 'required',
85
153
  hasUpgrade: true,
86
154
  required: true,
87
- targetsToUpdate: ['boot', 'app_v1', 'resource'],
155
+ targetsToUpdate: ['boot', 'app_v1'],
88
156
  components: [
89
157
  { configKey: 'bootloader', status: 'outdated', updateTarget: 'boot' },
90
158
  { configKey: 'applicationP1', status: 'outdated', updateTarget: 'app_v1' },
@@ -142,6 +210,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
142
210
  applicationP1Hash: 'aa'.repeat(32),
143
211
  applicationP2Hash: 'bb'.repeat(32),
144
212
  },
213
+ checkFirmwareHash: true,
145
214
  remotePayloadHashes: {
146
215
  applicationP1: 'aa'.repeat(64),
147
216
  applicationP2: 'cc'.repeat(64),
@@ -160,6 +229,37 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
160
229
  });
161
230
  });
162
231
 
232
+ test('uses version-only comparison for same-version packages by default', () => {
233
+ const packageSet: IFirmwareReleaseInfo = {
234
+ ...release,
235
+ required: false,
236
+ resourceBundles: undefined,
237
+ installOrder: ['applicationP1'],
238
+ components: {
239
+ applicationP1: {
240
+ target: 'APPLICATION_P1',
241
+ version: [1, 0, 0],
242
+ url: 'https://example.com/application-p1.okpkg',
243
+ payloadHash: 'cc'.repeat(64),
244
+ },
245
+ },
246
+ };
247
+
248
+ expect(
249
+ buildProtocolV2FirmwareRelease({
250
+ currentVersions: { ...currentVersions, applicationP1: '1.0.0' },
251
+ currentVerification: { applicationP1Hash: 'aa'.repeat(32) },
252
+ firmwareType: EFirmwareType.Universal,
253
+ release: packageSet,
254
+ })
255
+ ).toMatchObject({
256
+ status: 'valid',
257
+ hasUpgrade: false,
258
+ targetsToUpdate: [],
259
+ components: [{ configKey: 'applicationP1', status: 'valid' }],
260
+ });
261
+ });
262
+
163
263
  test('keeps same-version hash-aware components unknown without metadata payloadHash', () => {
164
264
  const packageSet: IFirmwareReleaseInfo = {
165
265
  ...release,
@@ -180,6 +280,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
180
280
  buildProtocolV2FirmwareRelease({
181
281
  currentVersions: { ...currentVersions, applicationP1: '1.0.0' },
182
282
  currentVerification: { applicationP1Hash: 'aa'.repeat(32) },
283
+ checkFirmwareHash: true,
183
284
  firmwareType: EFirmwareType.Universal,
184
285
  release: packageSet,
185
286
  })
@@ -215,6 +316,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
215
316
  const result = buildProtocolV2FirmwareRelease({
216
317
  currentVersions,
217
318
  currentVerification: { applicationP1Hash: 'aa'.repeat(32) },
319
+ checkFirmwareHash: true,
218
320
  remotePayloadHashes: { applicationP1: 'cc'.repeat(64) },
219
321
  firmwareType: EFirmwareType.Universal,
220
322
  release: packageSet,
@@ -223,7 +325,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
223
325
  expect(result.targetsToUpdate).toEqual(['app_v1', 'app_v2']);
224
326
  });
225
327
 
226
- test('uses the existing method on Protocol V2 and keeps its public API name', async () => {
328
+ test('does not read vol0 resource inventory while Protocol V2 is in Application mode', async () => {
227
329
  const method = new CheckAllFirmwareRelease({
228
330
  id: 1,
229
331
  payload: {
@@ -240,6 +342,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
240
342
  status: { mode: 'normal' },
241
343
  versions: currentVersions,
242
344
  });
345
+ const typedCall = createFilesystemTypedCall(true);
243
346
  method.device = {
244
347
  isProtocolV2: () => true,
245
348
  features: {
@@ -247,32 +350,124 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
247
350
  firmwareVersion: '1.0.0',
248
351
  },
249
352
  getDeviceState,
353
+ getCommands: () => ({ typedCall }),
250
354
  } as unknown as CheckAllFirmwareRelease['device'];
251
355
  jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
356
+ jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(stableResources as any);
252
357
 
253
358
  await expect(method.run()).resolves.toMatchObject({
254
359
  protocol: 'V2',
255
360
  deviceType: 'pro2',
256
361
  status: 'required',
257
- targetsToUpdate: ['boot', 'app_v1', 'resource'],
362
+ resourceStatus: 'unknown',
363
+ targetsToUpdate: ['boot', 'app_v1'],
258
364
  firmware: {
259
365
  status: 'required',
260
366
  },
261
367
  });
368
+ expect(typedCall).not.toHaveBeenCalled();
369
+ expect(getDeviceState).toHaveBeenCalledWith({
370
+ refreshSections: ['identity', 'versions'],
371
+ });
262
372
  expect(method.getSupportedProtocols()).toEqual(['V1', 'V2']);
263
373
  });
264
374
 
375
+ test('requests and compares firmware hashes only when explicitly enabled', async () => {
376
+ const packageSet: IFirmwareReleaseInfo = {
377
+ ...release,
378
+ required: false,
379
+ resourceBundles: undefined,
380
+ installOrder: ['applicationP1'],
381
+ components: {
382
+ applicationP1: {
383
+ target: 'APPLICATION_P1',
384
+ version: [1, 0, 0],
385
+ url: 'https://example.com/application-p1.okpkg',
386
+ payloadHash: 'cc'.repeat(64),
387
+ },
388
+ },
389
+ };
390
+ const method = new CheckAllFirmwareRelease({
391
+ id: 1,
392
+ payload: {
393
+ method: 'checkAllFirmwareRelease',
394
+ firmwareType: EFirmwareType.Universal,
395
+ checkFirmwareHash: true,
396
+ },
397
+ });
398
+ method.init();
399
+ const getDeviceState = jest.fn().mockResolvedValue({
400
+ identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
401
+ status: { mode: 'normal' },
402
+ versions: { ...currentVersions, applicationP1: '1.0.0' },
403
+ verification: { applicationP1Hash: 'aa'.repeat(32) },
404
+ });
405
+ method.device = {
406
+ isProtocolV2: () => true,
407
+ features: { deviceType: 'pro2', firmwareVersion: '1.0.0' },
408
+ getDeviceState,
409
+ getCommands: () => ({ typedCall: jest.fn() }),
410
+ } as unknown as CheckAllFirmwareRelease['device'];
411
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(packageSet);
412
+ jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(undefined);
413
+
414
+ await expect(method.run()).resolves.toMatchObject({
415
+ status: 'outdated',
416
+ targetsToUpdate: ['app_v1'],
417
+ });
418
+ expect(getDeviceState).toHaveBeenCalledWith({
419
+ refreshSections: ['identity', 'versions', 'verification'],
420
+ });
421
+ });
422
+
423
+ test.each(['bootloader', 'romloader'] as const)(
424
+ 'uses filesystem inventory in %s mode instead of forcing all resources',
425
+ async mode => {
426
+ const method = new CheckAllFirmwareRelease({
427
+ id: 1,
428
+ payload: {
429
+ method: 'checkAllFirmwareRelease',
430
+ firmwareType: EFirmwareType.Universal,
431
+ },
432
+ });
433
+ method.init();
434
+ const typedCall = createFilesystemTypedCall();
435
+ method.device = {
436
+ isProtocolV2: () => true,
437
+ features: { deviceType: 'pro2', firmwareVersion: '1.0.0' },
438
+ getDeviceState: jest.fn().mockResolvedValue({
439
+ identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
440
+ status: { mode },
441
+ versions: currentVersions,
442
+ }),
443
+ getCommands: () => ({ typedCall }),
444
+ } as unknown as CheckAllFirmwareRelease['device'];
445
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
446
+ jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(stableResources as any);
447
+
448
+ await expect(method.run()).resolves.toMatchObject({
449
+ protocol: 'V2',
450
+ resourceStatus: 'valid',
451
+ targetsToUpdate: ['boot', 'app_v1'],
452
+ });
453
+ expect(typedCall.mock.calls.some(call => call[0] === 'ResourceInventoryGet')).toBe(false);
454
+ expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileRead')).toBe(true);
455
+ }
456
+ );
457
+
265
458
  test('continues forwarding the existing public method', async () => {
266
459
  const call = jest.fn().mockResolvedValue({ success: true, payload: {} });
267
460
  const api = createCoreApi(call as CoreApi['call']) as CoreApi;
268
461
 
269
462
  await api.checkAllFirmwareRelease('pro2-connect-id', {
463
+ checkFirmwareHash: true,
270
464
  firmwareType: EFirmwareType.Universal,
271
465
  retryCount: 0,
272
466
  });
273
467
 
274
468
  expect(call).toHaveBeenCalledWith({
275
469
  connectId: 'pro2-connect-id',
470
+ checkFirmwareHash: true,
276
471
  firmwareType: EFirmwareType.Universal,
277
472
  retryCount: 0,
278
473
  method: 'checkAllFirmwareRelease',
@@ -1,61 +1,19 @@
1
- import DataManager from '../src/data-manager/DataManager';
2
1
  import { parseConnectSettings } from '../src/data-manager/connectSettings';
3
2
 
4
- import type { RemoteConfigResponse } from '../src/types';
5
-
6
- jest.mock('axios', () => ({
7
- get: jest.fn(),
8
- }));
9
-
10
3
  jest.mock('../src/data/config', () => ({
11
4
  getSDKVersion: jest.fn(() => '1.0.0'),
12
5
  DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
13
6
  }));
14
7
 
15
- const mockAxiosGet: jest.Mock = jest.requireMock('axios').get;
16
-
17
8
  describe('parseConnectSettings', () => {
18
- const preloadedConfig = {
19
- bridge: {},
20
- classic: { firmware: [], ble: [] },
21
- classic1s: { firmware: [], ble: [] },
22
- classicpure: { firmware: [], ble: [] },
23
- mini: { firmware: [], ble: [] },
24
- touch: { firmware: [], ble: [] },
25
- pro: { firmware: [], ble: [] },
26
- pro2: { firmware: [], ble: [] },
27
- } as unknown as RemoteConfigResponse;
28
-
29
- it('keeps a serializable preloaded config in parsed settings', () => {
30
- const settings = parseConnectSettings({
31
- firmwareManifestMode: 'sdk-managed',
32
- preloadedConfig,
33
- });
34
-
35
- expect(settings.firmwareManifestMode).toBe('sdk-managed');
36
- expect(settings.preloadedConfig).toBe(preloadedConfig);
37
- });
38
-
39
- it('uses the preloaded config before the SDK network client', async () => {
40
- const settings = parseConnectSettings({
41
- firmwareManifestMode: 'sdk-managed',
42
- preloadedConfig,
43
- });
44
-
45
- await DataManager.load(settings);
46
-
47
- expect(mockAxiosGet).not.toHaveBeenCalled();
48
- expect(DataManager.deviceMap.classic).toEqual({ firmware: [], ble: [] });
49
- });
9
+ it('keeps custom configFetcher in parsed settings', () => {
10
+ const configFetcher = jest.fn();
50
11
 
51
- it('fails closed without a preloaded config in external-only mode', async () => {
52
12
  const settings = parseConnectSettings({
53
- firmwareManifestMode: 'external-only',
13
+ fetchConfig: true,
14
+ configFetcher,
54
15
  });
55
16
 
56
- await expect(DataManager.load(settings)).rejects.toThrow(
57
- 'External firmware config snapshot is required'
58
- );
59
- expect(mockAxiosGet).not.toHaveBeenCalled();
17
+ expect(settings.configFetcher).toBe(configFetcher);
60
18
  });
61
19
  });
@@ -1,3 +1,4 @@
1
+ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
1
2
  import { TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
2
3
 
3
4
  import { initConnector, initCore } from '../src/core';
@@ -95,7 +96,7 @@ describe('public device lifecycle events', () => {
95
96
  expect(messages.filter(message => message.type === DEVICE.DISCONNECT)).toHaveLength(1);
96
97
  });
97
98
 
98
- test('uses a cached protocol as a fallback-capable acquire hint', async () => {
99
+ test('uses a verified cached protocol as a strict expected protocol', async () => {
99
100
  jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
100
101
  const device = Device.fromDescriptor({
101
102
  id: 'ble-id',
@@ -103,16 +104,117 @@ describe('public device lifecycle events', () => {
103
104
  commType: 'ble',
104
105
  protocolType: 'V1',
105
106
  } as never);
106
- const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id', protocolType: 'V2' });
107
+ const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id', protocolType: 'V1' });
107
108
  device.deviceConnector = { acquire } as never;
108
109
 
109
110
  await device.acquire();
110
111
 
111
- expect(acquire).toHaveBeenCalledWith('ble-id', undefined, true, undefined, 'V1');
112
+ expect(acquire).toHaveBeenCalledWith('ble-id', undefined, true, 'V1', undefined);
113
+ expect(device.getProtocol()).toBe('V1');
114
+ expect(device.originalDescriptor.protocolType).toBe('V1');
115
+ });
116
+
117
+ test('bypasses a cached protocol only for explicit active detection', async () => {
118
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
119
+ const device = Device.fromDescriptor({
120
+ id: 'ble-id',
121
+ path: 'ble-id',
122
+ commType: 'ble',
123
+ protocolType: 'V1',
124
+ } as never);
125
+ const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id', protocolType: 'V2' });
126
+ device.deviceConnector = { acquire } as never;
127
+
128
+ await device.acquire(undefined, { forceProtocolDetection: true });
129
+
130
+ expect(acquire).toHaveBeenCalledWith('ble-id', undefined, true, undefined, undefined);
112
131
  expect(device.getProtocol()).toBe('V2');
113
132
  expect(device.originalDescriptor.protocolType).toBe('V2');
114
133
  });
115
134
 
135
+ test('does not reuse a stale protocol when active detection returns no protocol', async () => {
136
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
137
+ const device = Device.fromDescriptor({
138
+ id: 'ble-id',
139
+ path: 'ble-id',
140
+ commType: 'ble',
141
+ protocolType: 'V1',
142
+ } as never);
143
+ const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id' });
144
+ const release = jest.fn().mockResolvedValue(undefined);
145
+ device.deviceConnector = { acquire, release } as never;
146
+ await expect(
147
+ device.acquire(undefined, {
148
+ forceProtocolDetection: true,
149
+ throwOnRunPromiseError: true,
150
+ })
151
+ ).rejects.toMatchObject({ errorCode: HardwareErrorCode.RuntimeError });
152
+ expect(device.originalDescriptor.protocolType).toBe('V1');
153
+ expect(device.hasDeviceAcquire()).toBe(false);
154
+ expect(release).toHaveBeenCalledWith('ble-id', false);
155
+ });
156
+
157
+ test('actively reacquires an already connected device without initializing it', async () => {
158
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue('webusb' as never);
159
+ const device = Device.fromDescriptor({
160
+ id: 'usb-device',
161
+ path: 'usb-device',
162
+ session: 'old-session',
163
+ protocolType: 'V1',
164
+ } as never);
165
+ device.mainId = 'old-session';
166
+ device.commands = { disposed: false } as never;
167
+ const release = jest.spyOn(device, 'release').mockImplementation(() => {
168
+ device.mainId = null;
169
+ device.originalDescriptor.session = null;
170
+ device.commands.disposed = true;
171
+ return Promise.resolve();
172
+ });
173
+ const acquire = jest.spyOn(device, 'acquire').mockImplementation(() => {
174
+ device.mainId = 'new-session';
175
+ device.originalDescriptor.session = 'new-session';
176
+ device.originalDescriptor.protocolType = 'V2';
177
+ device.commands.disposed = false;
178
+ return Promise.resolve();
179
+ });
180
+ const initialize = jest.spyOn(device, 'initialize').mockResolvedValue(undefined);
181
+
182
+ await device.run(() => Promise.resolve(undefined), {
183
+ forceProtocolDetection: true,
184
+ skipInitialize: true,
185
+ });
186
+
187
+ expect(release).toHaveBeenCalled();
188
+ expect(acquire).toHaveBeenCalledWith(undefined, {
189
+ forceProtocolDetection: true,
190
+ });
191
+ expect(initialize).not.toHaveBeenCalled();
192
+ expect(device.getProtocol()).toBe('V2');
193
+ });
194
+
195
+ test('keeps the freshly probed React Native BLE session until the call completes', async () => {
196
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
197
+ const device = Device.fromDescriptor({
198
+ id: 'ble-device',
199
+ path: 'ble-device',
200
+ commType: 'ble',
201
+ protocolType: 'V1',
202
+ } as never);
203
+ device.mainId = 'ble-device';
204
+ device.commands = { disposed: false } as never;
205
+ (device as unknown as { deviceAcquired: boolean }).deviceAcquired = true;
206
+ const release = jest.spyOn(device, 'release').mockResolvedValue(undefined);
207
+ const run = jest.fn().mockResolvedValue(undefined);
208
+
209
+ await device.run(run, {
210
+ forceProtocolDetection: true,
211
+ skipInitialize: true,
212
+ });
213
+
214
+ expect(run).toHaveBeenCalledTimes(1);
215
+ expect(release).toHaveBeenCalledTimes(1);
216
+ });
217
+
116
218
  test('keeps an explicit connectProtocol as a strict expected protocol', async () => {
117
219
  jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
118
220
  const device = Device.fromDescriptor({