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

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 (226) hide show
  1. package/__tests__/DeviceCommands.test.ts +87 -2
  2. package/__tests__/connectSettings.test.ts +47 -5
  3. package/__tests__/device-identity.test.ts +24 -2
  4. package/__tests__/device-lifecycle-events.test.ts +1 -1
  5. package/__tests__/device-settings.test.ts +111 -6
  6. package/__tests__/device-state-events.test.ts +3 -3
  7. package/__tests__/device-state-mapper.test.ts +15 -3
  8. package/__tests__/device-state-store.test.ts +1 -1
  9. package/__tests__/device-wallet-session-store.test.ts +111 -7
  10. package/__tests__/deviceSettings.test.ts +55 -15
  11. package/__tests__/evmLedgerLegacySafety.test.ts +6 -5
  12. package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
  13. package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
  14. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
  15. package/__tests__/firmware-update/firmware-update-plan.test.ts +326 -0
  16. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +200 -0
  17. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +39 -30
  18. package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
  19. package/__tests__/get-device-state.test.ts +7 -0
  20. package/__tests__/open-wallet-session.test.ts +253 -13
  21. package/__tests__/pro2Wallpaper.test.ts +7 -18
  22. package/__tests__/protocol-v2-unlock-policy.test.ts +45 -0
  23. package/__tests__/protocol-v2.test.ts +439 -282
  24. package/__tests__/public-device-state-api.test.ts +79 -68
  25. package/__tests__/public-pro2-api-boundary.test.ts +22 -29
  26. package/dist/api/BaseMethod.d.ts +2 -4
  27. package/dist/api/BaseMethod.d.ts.map +1 -1
  28. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  29. package/dist/api/ClearSessionCache.d.ts.map +1 -1
  30. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  31. package/dist/api/FirmwareUpdateV2.d.ts +11 -0
  32. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  33. package/dist/api/FirmwareUpdateV3.d.ts +7 -3
  34. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  35. package/dist/api/FirmwareUpdateV4.d.ts +29 -10
  36. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  37. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  38. package/dist/api/UploadPortfolio.d.ts +0 -1
  39. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  40. package/dist/api/conflux/ConfluxGetAddress.d.ts +1 -0
  41. package/dist/api/conflux/ConfluxGetAddress.d.ts.map +1 -1
  42. package/dist/api/conflux/ConfluxSignMessage.d.ts +1 -0
  43. package/dist/api/conflux/ConfluxSignMessage.d.ts.map +1 -1
  44. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts +1 -0
  45. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts.map +1 -1
  46. package/dist/api/conflux/ConfluxSignTransaction.d.ts +1 -0
  47. package/dist/api/conflux/ConfluxSignTransaction.d.ts.map +1 -1
  48. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  49. package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
  50. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  51. package/dist/api/firmware/FirmwareArtifactSource.d.ts +26 -0
  52. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
  53. package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
  54. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
  55. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +7 -0
  56. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  57. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
  58. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
  59. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +24 -0
  60. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
  61. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +25 -0
  62. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
  63. package/dist/api/firmware/getBinary.d.ts +7 -0
  64. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  65. package/dist/api/firmware/updateBootloader.d.ts +2 -0
  66. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  67. package/dist/api/firmware/uploadFirmware.d.ts +9 -1
  68. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  69. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  70. package/dist/api/index.d.ts +0 -20
  71. package/dist/api/index.d.ts.map +1 -1
  72. package/dist/api/nexa/NexaSignTransaction.d.ts +1 -0
  73. package/dist/api/nexa/NexaSignTransaction.d.ts.map +1 -1
  74. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  75. package/dist/core/RequestQueue.d.ts +1 -19
  76. package/dist/core/RequestQueue.d.ts.map +1 -1
  77. package/dist/core/index.d.ts +0 -1
  78. package/dist/core/index.d.ts.map +1 -1
  79. package/dist/data-manager/DataManager.d.ts +1 -0
  80. package/dist/data-manager/DataManager.d.ts.map +1 -1
  81. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  82. package/dist/device/Device.d.ts +1 -1
  83. package/dist/device/Device.d.ts.map +1 -1
  84. package/dist/device/DeviceCommands.d.ts.map +1 -1
  85. package/dist/device/DeviceFeaturesState.d.ts +0 -1
  86. package/dist/device/DeviceFeaturesState.d.ts.map +1 -1
  87. package/dist/device/DeviceStateMapper.d.ts.map +1 -1
  88. package/dist/device/DeviceStateProjector.d.ts.map +1 -1
  89. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  90. package/dist/events/call.d.ts +0 -7
  91. package/dist/events/call.d.ts.map +1 -1
  92. package/dist/events/core.d.ts +2 -2
  93. package/dist/events/core.d.ts.map +1 -1
  94. package/dist/events/iframe.d.ts +0 -1
  95. package/dist/events/iframe.d.ts.map +1 -1
  96. package/dist/events/logBlockEvent.d.ts.map +1 -1
  97. package/dist/index.d.ts +320 -277
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +3210 -1856
  100. package/dist/inject.d.ts +2 -3
  101. package/dist/inject.d.ts.map +1 -1
  102. package/dist/lowLevelInject.d.ts +1 -2
  103. package/dist/lowLevelInject.d.ts.map +1 -1
  104. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
  105. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +4 -0
  106. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -0
  107. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
  108. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  109. package/dist/topLevelInject.d.ts.map +1 -1
  110. package/dist/types/api/checkAllFirmwareRelease.d.ts +3 -0
  111. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  112. package/dist/types/api/deviceSettings.d.ts +2 -2
  113. package/dist/types/api/deviceSettings.d.ts.map +1 -1
  114. package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
  115. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  116. package/dist/types/api/export.d.ts +4 -1
  117. package/dist/types/api/export.d.ts.map +1 -1
  118. package/dist/types/api/firmwareUpdate.d.ts +69 -0
  119. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  120. package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
  121. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
  122. package/dist/types/api/firmwareUpdatePlan.d.ts +27 -0
  123. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
  124. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
  125. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
  126. package/dist/types/api/index.d.ts +10 -23
  127. package/dist/types/api/index.d.ts.map +1 -1
  128. package/dist/types/api/openWalletSession.d.ts +1 -0
  129. package/dist/types/api/openWalletSession.d.ts.map +1 -1
  130. package/dist/types/api/protocolV2.d.ts +3 -84
  131. package/dist/types/api/protocolV2.d.ts.map +1 -1
  132. package/dist/types/api/sessionCache.d.ts +4 -1
  133. package/dist/types/api/sessionCache.d.ts.map +1 -1
  134. package/dist/types/device.d.ts +4 -4
  135. package/dist/types/device.d.ts.map +1 -1
  136. package/dist/types/settings.d.ts +13 -0
  137. package/dist/types/settings.d.ts.map +1 -1
  138. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  139. package/dist/utils/deviceSettings.d.ts +36 -7
  140. package/dist/utils/deviceSettings.d.ts.map +1 -1
  141. package/dist/utils/index.d.ts +2 -1
  142. package/dist/utils/index.d.ts.map +1 -1
  143. package/dist/utils/patch.d.ts +1 -1
  144. package/dist/utils/patch.d.ts.map +1 -1
  145. package/package.json +4 -4
  146. package/src/api/BaseMethod.ts +11 -12
  147. package/src/api/CheckAllFirmwareRelease.ts +16 -3
  148. package/src/api/ClearSessionCache.ts +4 -0
  149. package/src/api/FirmwareUpdate.ts +8 -1
  150. package/src/api/FirmwareUpdateV2.ts +284 -119
  151. package/src/api/FirmwareUpdateV3.ts +248 -55
  152. package/src/api/FirmwareUpdateV4.ts +816 -328
  153. package/src/api/OpenWalletSession.ts +74 -25
  154. package/src/api/UploadPortfolio.ts +3 -13
  155. package/src/api/conflux/ConfluxGetAddress.ts +2 -0
  156. package/src/api/conflux/ConfluxSignMessage.ts +2 -0
  157. package/src/api/conflux/ConfluxSignMessageCIP23.ts +2 -0
  158. package/src/api/conflux/ConfluxSignTransaction.ts +2 -0
  159. package/src/api/device/DeviceChangePin.ts +1 -1
  160. package/src/api/device/DeviceSettings.ts +66 -5
  161. package/src/api/device/DeviceUpdateBootloader.ts +114 -6
  162. package/src/api/device/DeviceWipe.ts +1 -1
  163. package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
  164. package/src/api/firmware/FirmwareHostBinding.ts +100 -0
  165. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +50 -0
  166. package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
  167. package/src/api/firmware/FirmwareUpdatePlan.ts +621 -0
  168. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +378 -0
  169. package/src/api/firmware/updateBootloader.ts +19 -4
  170. package/src/api/firmware/uploadFirmware.ts +140 -22
  171. package/src/api/helpers/protocolV2FileWrite.ts +6 -0
  172. package/src/api/index.ts +0 -21
  173. package/src/api/nexa/NexaSignTransaction.ts +2 -0
  174. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +1 -1
  175. package/src/core/RequestQueue.ts +4 -123
  176. package/src/core/index.ts +25 -31
  177. package/src/data/messages/messages-protocol-v2.json +77 -4
  178. package/src/data-manager/DataManager.ts +33 -33
  179. package/src/data-manager/connectSettings.ts +11 -0
  180. package/src/device/Device.ts +51 -61
  181. package/src/device/DeviceCommands.ts +58 -14
  182. package/src/device/DeviceFeaturesState.ts +0 -9
  183. package/src/device/DeviceStateMapper.ts +26 -8
  184. package/src/device/DeviceStateProjector.ts +7 -1
  185. package/src/deviceProfile/buildDeviceFeatures.ts +2 -1
  186. package/src/events/call.ts +0 -6
  187. package/src/events/core.ts +0 -2
  188. package/src/events/iframe.ts +0 -1
  189. package/src/events/logBlockEvent.ts +1 -0
  190. package/src/index.ts +5 -4
  191. package/src/inject.ts +22 -35
  192. package/src/lowLevelInject.ts +5 -4
  193. package/src/protocols/protocol-v2/deviceSession.ts +1 -1
  194. package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +2 -2
  195. package/src/protocols/protocol-v2/unlockPolicy.ts +105 -0
  196. package/src/protocols/protocol-v2/unlockRetry.ts +1 -3
  197. package/src/protocols/protocol-v2/walletSession.ts +68 -12
  198. package/src/topLevelInject.ts +5 -3
  199. package/src/types/api/checkAllFirmwareRelease.ts +3 -0
  200. package/src/types/api/deviceSettings.ts +17 -2
  201. package/src/types/api/deviceUpdateBootloader.ts +6 -0
  202. package/src/types/api/export.ts +18 -0
  203. package/src/types/api/firmwareUpdate.ts +76 -0
  204. package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
  205. package/src/types/api/firmwareUpdatePlan.ts +36 -0
  206. package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
  207. package/src/types/api/index.ts +14 -41
  208. package/src/types/api/openWalletSession.ts +2 -0
  209. package/src/types/api/protocolV2.ts +3 -187
  210. package/src/types/api/sessionCache.ts +9 -4
  211. package/src/types/device.ts +5 -3
  212. package/src/types/settings.ts +13 -0
  213. package/src/utils/deviceFeaturesUtils.ts +5 -1
  214. package/src/utils/deviceSettings.ts +130 -56
  215. package/src/utils/index.ts +18 -1
  216. package/__tests__/RequestQueue.test.ts +0 -140
  217. package/__tests__/UploadPortfolio.test.ts +0 -46
  218. package/dist/api/protocol-v2/DeviceSettingsGet.d.ts +0 -6
  219. package/dist/api/protocol-v2/DeviceSettingsGet.d.ts.map +0 -1
  220. package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts +0 -16
  221. package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts.map +0 -1
  222. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts +0 -12
  223. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +0 -1
  224. package/src/api/protocol-v2/DeviceSettingsGet.ts +0 -18
  225. package/src/api/protocol-v2/DeviceSettingsPageShow.ts +0 -70
  226. package/src/api/protocol-v2/DeviceSettingsSet.ts +0 -44
@@ -34,6 +34,25 @@ const wasResumed = (session: unknown) =>
34
34
  'resumed' in session &&
35
35
  (session as { resumed?: unknown }).resumed === true;
36
36
 
37
+ const forwardSessionId = (session: { newSession?: string }) =>
38
+ session.newSession ? { sessionId: session.newSession } : {};
39
+
40
+ const requireHiddenWalletResponse = (session: {
41
+ passphraseState?: string;
42
+ newSession?: string;
43
+ }) => {
44
+ if (!session.passphraseState) {
45
+ throw ERRORS.TypedError(
46
+ HardwareErrorCode.DeviceInitializeFailed,
47
+ 'Hidden wallet response is missing passphraseState'
48
+ );
49
+ }
50
+ return {
51
+ passphraseState: session.passphraseState,
52
+ ...forwardSessionId(session),
53
+ };
54
+ };
55
+
37
56
  type NormalizedOpenWalletSessionParams = OpenWalletSessionParams & {
38
57
  legacySessionToClear?: {
39
58
  deviceId?: string;
@@ -117,30 +136,47 @@ export default class OpenWalletSession extends BaseMethod<NormalizedOpenWalletSe
117
136
  }
118
137
 
119
138
  async run(): Promise<OpenWalletSessionPayload> {
139
+ const isProtocolV2 = this.device.isProtocolV2();
120
140
  const state = await this.device.getDeviceState({ refreshSections: ['status'] });
121
- const currentDeviceId = state.identity.deviceId;
122
- if (!currentDeviceId) {
123
- throw ERRORS.TypedError(HardwareErrorCode.DeviceInitializeFailed);
124
- }
125
-
141
+ let currentDeviceId = state.identity.deviceId;
126
142
  const { legacySessionToClear } = this.params;
127
- if (
128
- legacySessionToClear &&
129
- (!legacySessionToClear.deviceId || legacySessionToClear.deviceId === currentDeviceId)
130
- ) {
131
- deviceWalletSessionStore.delete(currentDeviceId, legacySessionToClear.passphraseState);
143
+ const requireDeviceId = () => {
144
+ if (!currentDeviceId) {
145
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceInitializeFailed);
146
+ }
147
+ return currentDeviceId;
148
+ };
149
+ const clearLegacySession = () => {
150
+ const deviceId = requireDeviceId();
151
+ if (
152
+ legacySessionToClear &&
153
+ (!legacySessionToClear.deviceId || legacySessionToClear.deviceId === deviceId)
154
+ ) {
155
+ deviceWalletSessionStore.delete(deviceId, legacySessionToClear.passphraseState);
156
+ }
157
+ };
158
+ const refreshProtocolV2DeviceId = async () => {
159
+ const refreshedState = await this.device.getDeviceState({ refreshSections: ['status'] });
160
+ currentDeviceId = refreshedState.identity.deviceId;
161
+ clearLegacySession();
162
+ return requireDeviceId();
163
+ };
164
+
165
+ if (!isProtocolV2) {
166
+ clearLegacySession();
132
167
  }
133
168
 
134
- const protocol = this.device.isProtocolV2() ? 'V2' : 'V1';
169
+ const protocol = isProtocolV2 ? 'V2' : 'V1';
135
170
 
136
171
  if (this.params.mode === 'standard') {
137
172
  this.device.passphraseState = undefined;
138
- const session = this.device.isProtocolV2()
173
+ const session = isProtocolV2
139
174
  ? await getProtocolV2WalletSession(this.device, { onlyMainPin: true })
140
175
  : await getPassphraseStateWithRefreshDeviceInfo(this.device, {
141
176
  onlyMainPin: true,
142
177
  initSession: this.payload.initSession,
143
178
  });
179
+ const deviceId = isProtocolV2 ? await refreshProtocolV2DeviceId() : requireDeviceId();
144
180
  if (session.unlockedAttachPin) {
145
181
  try {
146
182
  await this.device.lockDevice();
@@ -153,14 +189,24 @@ export default class OpenWalletSession extends BaseMethod<NormalizedOpenWalletSe
153
189
  return {
154
190
  protocol,
155
191
  walletType: 'standard',
156
- deviceId: currentDeviceId,
192
+ deviceId,
157
193
  passphraseState: null,
194
+ ...forwardSessionId(session),
158
195
  resumed: wasResumed(session),
159
196
  };
160
197
  }
161
198
 
162
199
  if (this.params.mode === 'resume-hidden') {
163
- if (currentDeviceId !== this.params.deviceId) {
200
+ if (isProtocolV2) {
201
+ if (this.device.features?.unlocked === false) {
202
+ await this.device.unlockDevice();
203
+ }
204
+ const refreshedDeviceId = await refreshProtocolV2DeviceId();
205
+ if (refreshedDeviceId !== this.params.deviceId) {
206
+ deviceWalletSessionStore.delete(this.params.deviceId, this.params.passphraseState);
207
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
208
+ }
209
+ } else if (requireDeviceId() !== this.params.deviceId) {
164
210
  throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
165
211
  }
166
212
  this.device.passphraseState = this.params.passphraseState;
@@ -171,19 +217,20 @@ export default class OpenWalletSession extends BaseMethod<NormalizedOpenWalletSe
171
217
  if (!cachedSessionId) {
172
218
  throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
173
219
  }
174
- if (!this.device.isProtocolV2()) {
220
+ if (!isProtocolV2) {
175
221
  await this.device.initialize({
176
222
  deviceId: this.params.deviceId,
177
223
  passphraseState: this.params.passphraseState,
178
224
  });
179
225
  }
180
- const session = this.device.isProtocolV2()
226
+ const session = isProtocolV2
181
227
  ? await getProtocolV2WalletSession(this.device, {
182
228
  expectedPassphraseState: this.params.passphraseState,
183
229
  })
184
230
  : await getPassphraseStateWithRefreshDeviceInfo(this.device, {
185
231
  expectPassphraseState: this.params.passphraseState,
186
232
  });
233
+ const deviceId = requireDeviceId();
187
234
  if (!session.passphraseState) {
188
235
  this.device.clearInternalState();
189
236
  throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
@@ -191,31 +238,33 @@ export default class OpenWalletSession extends BaseMethod<NormalizedOpenWalletSe
191
238
  return {
192
239
  protocol,
193
240
  walletType: 'hidden',
194
- deviceId: currentDeviceId,
195
- passphraseState: session.passphraseState ?? null,
241
+ deviceId,
242
+ ...requireHiddenWalletResponse(session),
196
243
  resumed: wasResumed(session) || session.newSession === cachedSessionId,
197
244
  };
198
245
  }
199
246
 
200
247
  this.device.passphraseState = undefined;
201
- const session = this.device.isProtocolV2()
248
+ const session = isProtocolV2
202
249
  ? await getProtocolV2WalletSession(this.device, { initSession: true })
203
250
  : await getPassphraseStateWithRefreshDeviceInfo(this.device, { initSession: true });
251
+ const deviceId = isProtocolV2 ? await refreshProtocolV2DeviceId() : requireDeviceId();
204
252
  const responseBase = {
205
253
  protocol,
206
- deviceId: currentDeviceId,
254
+ deviceId,
207
255
  resumed: wasResumed(session),
208
256
  } as const;
209
- return session.passphraseState
257
+ return this.device.getCurrentPassphraseProtection() === false
210
258
  ? {
211
259
  ...responseBase,
212
- walletType: 'hidden',
213
- passphraseState: session.passphraseState,
260
+ walletType: 'standard',
261
+ passphraseState: null,
262
+ ...forwardSessionId(session),
214
263
  }
215
264
  : {
216
265
  ...responseBase,
217
- walletType: 'standard',
218
- passphraseState: null,
266
+ walletType: 'hidden',
267
+ ...requireHiddenWalletResponse(session),
219
268
  };
220
269
  }
221
270
  }
@@ -2,22 +2,15 @@ import FileWrite from './FileWrite';
2
2
 
3
3
  export type UploadPortfolioParams = {
4
4
  packageBytes: ArrayBuffer | Uint8Array | Blob;
5
- operationId?: string;
6
5
  timeoutMs?: number | string;
7
6
  };
8
7
 
9
8
  const PORTFOLIO_PENDING_PATH = 'vol1:/portfolio/portfolio.okpkg.pending';
10
9
  const PORTFOLIO_CHUNK_SIZE = 2048;
11
- const PORTFOLIO_RESPONSE_TIMEOUT_MS = 5_000;
12
- const PORTFOLIO_MAX_ABORT_LATENCY_MS = PORTFOLIO_RESPONSE_TIMEOUT_MS * 2 + 2_000;
13
10
 
14
11
  export default class UploadPortfolio extends FileWrite {
15
12
  init() {
16
13
  const { packageBytes, timeoutMs } = this.payload as UploadPortfolioParams;
17
- const responseTimeoutMs = Math.min(
18
- timeoutMs === undefined ? PORTFOLIO_RESPONSE_TIMEOUT_MS : Number(timeoutMs),
19
- PORTFOLIO_RESPONSE_TIMEOUT_MS
20
- );
21
14
  this.payload = {
22
15
  ...this.payload,
23
16
  path: PORTFOLIO_PENDING_PATH,
@@ -27,12 +20,10 @@ export default class UploadPortfolio extends FileWrite {
27
20
  overwrite: true,
28
21
  append: false,
29
22
  emitProgress: false,
30
- timeoutMs: responseTimeoutMs,
23
+ timeoutMs,
31
24
  };
32
25
  super.init();
33
- this.executionPriority = 'background';
34
- this.maxAbortLatencyMs = PORTFOLIO_MAX_ABORT_LATENCY_MS;
35
- this.unlockPolicy = 'retry-on-locked';
26
+ this.unlockPolicy = 'unlock-before-run';
36
27
  // Portfolio is a background write/apply flow and never synthesizes UI events.
37
28
  this.protocolV2UiMode = 'none';
38
29
  }
@@ -40,8 +31,7 @@ export default class UploadPortfolio extends FileWrite {
40
31
  async run() {
41
32
  const stagedFile = await super.run();
42
33
  this.throwIfAborted();
43
- const timeoutMs = Number(this.params.timeoutMs);
44
- await this.device.commands.typedCall('PortfolioUpdate', 'Success', {}, { timeoutMs });
34
+ await this.device.commands.typedCall('PortfolioUpdate', 'Success', {});
45
35
  return {
46
36
  ...stagedFile,
47
37
  portfolioUpdated: true,
@@ -7,6 +7,8 @@ import type { ConfluxGetAddress as HardwareConfluxGetAddress } from '@onekeyfe/h
7
7
  import type { ConfluxAddress, ConfluxGetAddressParams } from '../../types/api/confluxGetAddress';
8
8
 
9
9
  export default class ConfluxGetAddress extends BaseMethod<HardwareConfluxGetAddress[]> {
10
+ checkDeviceId = true;
11
+
10
12
  hasBundle = false;
11
13
 
12
14
  init() {
@@ -7,6 +7,8 @@ import { formatAnyHex } from '../helpers/hexUtils';
7
7
  import type { ConfluxSignMessage as HardwareConfluxSignMessage } from '@onekeyfe/hd-transport';
8
8
 
9
9
  export default class ConfluxSignMessage extends BaseMethod<HardwareConfluxSignMessage> {
10
+ checkDeviceId = true;
11
+
10
12
  init() {
11
13
  this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.NOT_INITIALIZE];
12
14
  this.allowUsePreInitialize = true;
@@ -7,6 +7,8 @@ import { formatAnyHex } from '../helpers/hexUtils';
7
7
  import type { ConfluxSignMessageCIP23 as HardwareConfluxSignMessageCIP23 } from '@onekeyfe/hd-transport';
8
8
 
9
9
  export default class ConfluxSignMessageCIP23 extends BaseMethod<HardwareConfluxSignMessageCIP23> {
10
+ checkDeviceId = true;
11
+
10
12
  init() {
11
13
  this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.NOT_INITIALIZE];
12
14
  this.allowUsePreInitialize = true;
@@ -16,6 +16,8 @@ import type {
16
16
  import type { ConfluxSignTx, ConfluxTxRequest } from '@onekeyfe/hd-transport';
17
17
 
18
18
  export default class ConfluxSignTransaction extends BaseMethod {
19
+ checkDeviceId = true;
20
+
19
21
  addressN: number[] = [];
20
22
 
21
23
  formattedTx: ConfluxTransaction | undefined;
@@ -8,7 +8,7 @@ import type { ChangePin } from '@onekeyfe/hd-transport';
8
8
 
9
9
  export default class DeviceChangePin extends BaseMethod<ChangePin> {
10
10
  init() {
11
- this.unlockPolicy = 'retry-on-locked';
11
+ this.unlockPolicy = 'unlock-before-run';
12
12
  this.useDevicePassphraseState = false;
13
13
 
14
14
  // check payload
@@ -10,11 +10,56 @@ import {
10
10
  mapDeviceSettingsToState,
11
11
  } from '../../device/DeviceStateMapper';
12
12
  import { getProtocolV2SettingsBehavior } from '../../protocols/protocol-v2/settingsUnlockPolicy';
13
- import { LANGUAGE_LABELS } from '../../utils/deviceSettings';
13
+ import {
14
+ DEVICE_SETTINGS_V1_ONLY_FIELDS,
15
+ DEVICE_SETTINGS_V2_ONLY_FIELDS,
16
+ LANGUAGE_LABELS,
17
+ getDeviceSettingsCapabilities,
18
+ } from '../../utils/deviceSettings';
14
19
 
15
20
  import type { ApplySettings } from '@onekeyfe/hd-transport';
16
21
  import type { DeviceSettingsParams } from '../../types/api/deviceSettings';
17
22
 
23
+ const assertSettingsSupported = (
24
+ payload: DeviceSettingsParams,
25
+ unsupported: readonly (keyof DeviceSettingsParams)[],
26
+ protocol: 'Protocol V1' | 'Protocol V2'
27
+ ) => {
28
+ const provided = unsupported.filter(key => payload[key] !== undefined);
29
+ if (provided.length > 0) {
30
+ throw invalidParameter(`${protocol} does not support settings: ${provided.join(', ')}.`);
31
+ }
32
+ };
33
+
34
+ const assertProtocolV2SettingValues = (
35
+ payload: DeviceSettingsParams,
36
+ capabilities: ReturnType<typeof getDeviceSettingsCapabilities>
37
+ ) => {
38
+ const brightnessRange = capabilities.ranges.brightness;
39
+ if (
40
+ payload.brightness !== undefined &&
41
+ brightnessRange &&
42
+ (payload.brightness < brightnessRange.min || payload.brightness > brightnessRange.max)
43
+ ) {
44
+ throw invalidParameter(
45
+ `Protocol V2 brightness must be between ${brightnessRange.min} and ${brightnessRange.max}.`
46
+ );
47
+ }
48
+
49
+ const delayFields = [
50
+ ['autoLockDelayMs', capabilities.autoLockDelayOptions],
51
+ ['autoShutdownDelayMs', capabilities.autoShutdownDelayOptions],
52
+ ] as const;
53
+ delayFields.forEach(([field, options]) => {
54
+ const value = payload[field];
55
+ if (value !== undefined && !options.some(option => option.valueMs === value)) {
56
+ throw invalidParameter(
57
+ `Protocol V2 ${field} must be one of: ${options.map(option => option.valueMs).join(', ')}.`
58
+ );
59
+ }
60
+ });
61
+ };
62
+
18
63
  const settingsPageForPayload = (
19
64
  payload: DeviceSettingsParams
20
65
  ): DeviceSettingsPage.DevicePassphrase | DeviceSettingsPage.DeviceAirgap | undefined => {
@@ -70,6 +115,7 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
70
115
  safety_checks: this.payload.safetyChecks,
71
116
  experimental_features: this.payload.experimentalFeatures,
72
117
  auto_shutdown_delay_ms: this.payload.autoShutdownDelayMs,
118
+ use_ble: this.payload.bluetoothEnabled,
73
119
  ...(this.payload.changeBrightness
74
120
  ? { change_brightness: this.payload.changeBrightness }
75
121
  : undefined),
@@ -113,6 +159,12 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
113
159
  async run() {
114
160
  try {
115
161
  if (this.device.isProtocolV2()) {
162
+ assertSettingsSupported(this.payload, DEVICE_SETTINGS_V1_ONLY_FIELDS, 'Protocol V2');
163
+ const capabilities = getDeviceSettingsCapabilities(
164
+ this.device.getCurrentDeviceType(),
165
+ 'V2'
166
+ );
167
+ assertProtocolV2SettingValues(this.payload, capabilities);
116
168
  const settings = mapCommonSettingsToProtocolV2(this.payload);
117
169
  const requestedPassphrase = this.payload.usePassphrase;
118
170
  const requestedAirgap = this.payload.airgapMode;
@@ -172,18 +224,27 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
172
224
  const res = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
173
225
  settings,
174
226
  });
175
- this.device.updateState(mapDeviceSettingsToState(settings), 'apply-settings');
227
+ this.device.updateState(mapDeviceSettingsToState(settings), 'settings-write');
176
228
  return res.message;
177
229
  }
178
230
 
179
- if (this.payload.airgapMode !== undefined) {
180
- throw invalidParameter('Air-gap settings are not supported by Protocol V1.');
231
+ assertSettingsSupported(this.payload, DEVICE_SETTINGS_V2_ONLY_FIELDS, 'Protocol V1');
232
+ const capabilities = getDeviceSettingsCapabilities(this.device.getCurrentDeviceType(), 'V1');
233
+ if (
234
+ this.payload.safetyChecks !== undefined &&
235
+ !capabilities.safetyCheckOptions.some(option => option.value === this.payload.safetyChecks)
236
+ ) {
237
+ throw invalidParameter(
238
+ `Protocol V1 safetyChecks must be one of: ${capabilities.safetyCheckOptions
239
+ .map(option => option.value)
240
+ .join(', ')}.`
241
+ );
181
242
  }
182
243
 
183
244
  const res = await this.device.commands.typedCall('ApplySettings', 'Success', {
184
245
  ...this.params,
185
246
  });
186
- this.device.updateState(mapApplySettingsToState(this.params), 'apply-settings');
247
+ this.device.updateState(mapApplySettingsToState(this.params), 'settings-write');
187
248
  return res.message;
188
249
  } catch (error) {
189
250
  if (error.message?.toLowerCase().includes('no setting provided')) {
@@ -5,15 +5,19 @@ import { UI_REQUEST } from '../../constants/ui-request';
5
5
  import { FirmwareUpdateTipMessage } from '../../events/ui-request';
6
6
  import { FirmwareUpdateBaseMethod } from '../firmware/FirmwareUpdateBaseMethod';
7
7
  import { getSysResourceBinary } from '../firmware/getBinary';
8
- import { updateBootloader } from '../firmware/uploadFirmware';
8
+ import { updateBootloader, updateBootloaderFromSource } from '../firmware/uploadFirmware';
9
9
  import { DeviceModelToTypes } from '../../types';
10
10
  import { DataManager } from '../../data-manager';
11
- import { checkBootloaderLength } from '../firmware/updateBootloader';
11
+ import { checkBootloaderLength, checkBootloaderSourceLength } from '../firmware/updateBootloader';
12
+ import { openFirmwareByteSource } from '../firmware/FirmwareArtifactSource';
13
+ import { resolveFirmwareUpdateHostBinding } from '../firmware/FirmwareHostBinding';
14
+ import { assertFirmwareUpdatePreparedPlanBinding } from '../firmware/FirmwareUpdatePreparedPlan';
12
15
 
13
16
  import type { DeviceUpdateBootloaderParams } from '../../types/api/deviceUpdateBootloader';
14
17
  import type { EFirmwareType } from '@onekeyfe/hd-shared';
15
18
  import type { Device } from '../../device/Device';
16
19
  import type { Features } from '../../types';
20
+ import type { FirmwareByteSource } from '../firmware/FirmwareArtifactSource';
17
21
 
18
22
  export default class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod<any> {
19
23
  init() {
@@ -43,17 +47,68 @@ export default class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod<any
43
47
  return true;
44
48
  }
45
49
 
50
+ async updateBootloaderWithEmmcFileWriteFromSource(_device: Device, source: FirmwareByteSource) {
51
+ const filePath = '0:boot/bootloader.bin';
52
+
53
+ this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
54
+ await this.emmcCommonUpdateProcessFromSource({
55
+ source,
56
+ filePath,
57
+ });
58
+ this.postTipMessage(FirmwareUpdateTipMessage.ConfirmOnDevice);
59
+ await this.reboot(RebootType.Normal);
60
+ this.postTipMessage(FirmwareUpdateTipMessage.UpdateBootloaderSuccess);
61
+ return true;
62
+ }
63
+
46
64
  async updateTouchBootloader({
47
65
  device,
48
66
  features,
49
67
  firmwareType,
68
+ binary: preparedBinary,
69
+ source: preparedSource,
50
70
  }: {
51
71
  device: Device;
52
72
  features?: Features;
53
73
  firmwareType: EFirmwareType;
74
+ binary?: ArrayBuffer;
75
+ source?: FirmwareByteSource;
54
76
  }) {
55
- let { binary } = this.payload;
77
+ if (preparedSource) {
78
+ if (!(await checkBootloaderSourceLength(preparedSource))) {
79
+ throw ERRORS.TypedError(HardwareErrorCode.CheckDownloadFileError);
80
+ }
81
+ if (features && device.isBootloader()) {
82
+ this.postTipMessage(FirmwareUpdateTipMessage.UpdateBootloader);
83
+ const result = await this.updateBootloaderWithEmmcFileWriteFromSource(
84
+ device,
85
+ preparedSource
86
+ );
87
+ return result;
88
+ }
89
+ if (features && !device.isBootloader()) {
90
+ await updateBootloaderFromSource(
91
+ this.device.getCommands().typedCall.bind(this.device.getCommands()),
92
+ this.postMessage,
93
+ device,
94
+ preparedSource
95
+ );
96
+ return true;
97
+ }
98
+ }
99
+
100
+ let binary = preparedBinary;
56
101
  if (!binary) {
102
+ if (DataManager.getSettings('firmwareManifestMode') === 'external-only') {
103
+ throw ERRORS.TypedError(
104
+ HardwareErrorCode.RuntimeError,
105
+ 'Bootloader must be prepared by the external firmware host',
106
+ {
107
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
108
+ artifactName: 'bootloader',
109
+ }
110
+ );
111
+ }
57
112
  this.postTipMessage(FirmwareUpdateTipMessage.CheckLatestUiResource);
58
113
  const resourceUrl = features
59
114
  ? DataManager.getBootloaderResource(features, firmwareType)
@@ -68,7 +123,7 @@ export default class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod<any
68
123
  }
69
124
  }
70
125
 
71
- if (!checkBootloaderLength(binary)) {
126
+ if (!binary || !checkBootloaderLength(binary)) {
72
127
  throw ERRORS.TypedError(HardwareErrorCode.CheckDownloadFileError);
73
128
  }
74
129
 
@@ -76,7 +131,8 @@ export default class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod<any
76
131
  if (features && device.isBootloader()) {
77
132
  // Use emmcFileWrite + reboot logic for bootloader mode
78
133
  this.postTipMessage(FirmwareUpdateTipMessage.UpdateBootloader);
79
- return this.updateBootloaderWithEmmcFileWrite(device, binary);
134
+ const result = await this.updateBootloaderWithEmmcFileWrite(device, binary);
135
+ return result;
80
136
  }
81
137
 
82
138
  if (features && !device.isBootloader()) {
@@ -96,13 +152,65 @@ export default class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod<any
96
152
  const { features } = device;
97
153
 
98
154
  const payload = this.payload as DeviceUpdateBootloaderParams;
155
+ const hostBinding = payload.artifact
156
+ ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
157
+ : undefined;
158
+ const executionParams = {
159
+ ...payload,
160
+ artifactReader: hostBinding?.artifactReader ?? payload.artifactReader,
161
+ };
162
+ if (payload.artifact) {
163
+ assertFirmwareUpdatePreparedPlanBinding({
164
+ preparedPlan: payload.preparedPlan,
165
+ executor: 'v2',
166
+ scopeTargets: ['bootloader'],
167
+ bindings: [
168
+ {
169
+ target: 'bootloader',
170
+ artifact: payload.artifact,
171
+ },
172
+ ],
173
+ });
174
+ const source = await openFirmwareByteSource({
175
+ artifact: payload.artifact,
176
+ reader: executionParams.artifactReader,
177
+ });
178
+ if (!source) {
179
+ throw ERRORS.TypedError(
180
+ HardwareErrorCode.RuntimeError,
181
+ 'Bootloader artifact is not prepared'
182
+ );
183
+ }
184
+ try {
185
+ const deviceType = device.getCurrentDeviceType();
186
+ const deviceFirmwareType = device.getCurrentFirmwareType();
187
+ const firmwareType = payload.firmwareType ?? deviceFirmwareType;
188
+ if (DeviceModelToTypes.model_touch.includes(deviceType)) {
189
+ return await this.updateTouchBootloader({
190
+ device,
191
+ features,
192
+ firmwareType,
193
+ source,
194
+ });
195
+ }
196
+ return true;
197
+ } finally {
198
+ await source.close();
199
+ }
200
+ }
99
201
 
202
+ const { binary } = payload;
100
203
  const deviceType = device.getCurrentDeviceType();
101
204
  const deviceFirmwareType = device.getCurrentFirmwareType();
102
205
  const firmwareType = payload.firmwareType ?? deviceFirmwareType;
103
206
 
104
207
  if (DeviceModelToTypes.model_touch.includes(deviceType)) {
105
- return this.updateTouchBootloader({ device, features, firmwareType });
208
+ return this.updateTouchBootloader({
209
+ device,
210
+ features,
211
+ firmwareType,
212
+ binary,
213
+ });
106
214
  }
107
215
 
108
216
  return Promise.resolve(true);
@@ -6,7 +6,7 @@ import type { WipeDevice } from '@onekeyfe/hd-transport';
6
6
 
7
7
  export default class DeviceWipe extends BaseMethod<WipeDevice> {
8
8
  init() {
9
- this.unlockPolicy = 'retry-on-locked';
9
+ this.unlockPolicy = 'unlock-before-run';
10
10
  this.useDevicePassphraseState = false;
11
11
  this.protocolV2UiInteraction = {
12
12
  request: 'button',