@onekeyfe/hd-core 1.2.0-alpha.99 → 1.2.0

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 (200) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +20 -4
  2. package/__tests__/DeviceCommands.test.ts +203 -27
  3. package/__tests__/base64Data.test.ts +69 -0
  4. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +48 -2
  5. package/__tests__/check-firmware-release-protocol-v2.test.ts +82 -13
  6. package/__tests__/device-connector-protocol.test.ts +21 -0
  7. package/__tests__/device-lifecycle-events.test.ts +427 -7
  8. package/__tests__/device-pool-state.test.ts +25 -0
  9. package/__tests__/device-settings.test.ts +237 -9
  10. package/__tests__/device-state-events.test.ts +2 -2
  11. package/__tests__/device-state-mapper.test.ts +61 -5
  12. package/__tests__/device-utils.test.ts +15 -1
  13. package/__tests__/device-wallet-session-store.test.ts +147 -21
  14. package/__tests__/deviceSettings.test.ts +7 -3
  15. package/__tests__/deviceUploadNft.test.ts +36 -4
  16. package/__tests__/firmware-update/device-update-bootloader.test.ts +13 -3
  17. package/__tests__/firmware-update/firmware-artifact-source.test.ts +61 -9
  18. package/__tests__/firmware-update/firmware-prepared-host-digest.test.ts +19 -9
  19. package/__tests__/firmware-update/firmware-prepared-resource-archive.test.ts +21 -0
  20. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +63 -0
  21. package/__tests__/firmware-update/firmware-update-plan.test.ts +59 -55
  22. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +19 -5
  23. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +116 -0
  24. package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +678 -0
  25. package/__tests__/firmware-update/firmware-update-v4-parameters.test.ts +85 -0
  26. package/__tests__/get-device-state.test.ts +60 -21
  27. package/__tests__/logBlockEvent.test.ts +13 -1
  28. package/__tests__/open-wallet-session.test.ts +127 -1
  29. package/__tests__/protocol-v2-firmware-targets.test.ts +23 -0
  30. package/__tests__/protocol-v2-resources.test.ts +185 -88
  31. package/__tests__/protocol-v2-unlock-policy.test.ts +235 -2
  32. package/__tests__/protocol-v2.test.ts +2198 -486
  33. package/__tests__/refresh-device-state.test.ts +3 -1
  34. package/__tests__/resourceBase64Boundary.test.ts +48 -0
  35. package/__tests__/search-devices.test.ts +79 -32
  36. package/__tests__/ton-sign-message.test.ts +84 -0
  37. package/dist/api/BaseMethod.d.ts +2 -1
  38. package/dist/api/BaseMethod.d.ts.map +1 -1
  39. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  40. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  41. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  42. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  43. package/dist/api/FirmwareUpdateV4.d.ts +19 -6
  44. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  45. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  46. package/dist/api/SearchDevices.d.ts.map +1 -1
  47. package/dist/api/UploadPortfolio.d.ts +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/DeviceRebootToBoardloader.d.ts.map +1 -1
  52. package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
  53. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  54. package/dist/api/device/DeviceVerify.d.ts +1 -0
  55. package/dist/api/device/DeviceVerify.d.ts.map +1 -1
  56. package/dist/api/device/DeviceWipe.d.ts.map +1 -1
  57. package/dist/api/firmware/FirmwareArtifactSource.d.ts +5 -1
  58. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -1
  59. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts.map +1 -1
  60. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
  61. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  62. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -15
  63. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  64. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
  65. package/dist/api/firmware/getBinary.d.ts +3 -2
  66. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  67. package/dist/api/firmware/protocolV2Release.d.ts.map +1 -1
  68. package/dist/api/helpers/base64Data.d.ts +16 -0
  69. package/dist/api/helpers/base64Data.d.ts.map +1 -0
  70. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +3 -1
  71. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
  72. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  73. package/dist/api/protocol-v2/DeviceInfoGet.d.ts +1 -1
  74. package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
  75. package/dist/api/protocol-v2/DeviceReboot.d.ts +1 -1
  76. package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
  77. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +2 -3
  78. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
  79. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +2 -3
  80. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  81. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  82. package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
  83. package/dist/core/index.d.ts +6 -1
  84. package/dist/core/index.d.ts.map +1 -1
  85. package/dist/data-manager/DataManager.d.ts +4 -3
  86. package/dist/data-manager/DataManager.d.ts.map +1 -1
  87. package/dist/data-manager/TransportManager.d.ts.map +1 -1
  88. package/dist/device/Device.d.ts +16 -3
  89. package/dist/device/Device.d.ts.map +1 -1
  90. package/dist/device/DeviceCommands.d.ts +4 -4
  91. package/dist/device/DeviceCommands.d.ts.map +1 -1
  92. package/dist/device/DeviceConnector.d.ts +1 -1
  93. package/dist/device/DeviceConnector.d.ts.map +1 -1
  94. package/dist/device/DevicePool.d.ts.map +1 -1
  95. package/dist/device/DeviceStateMapper.d.ts.map +1 -1
  96. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  97. package/dist/events/logBlockEvent.d.ts.map +1 -1
  98. package/dist/events/ui-request.d.ts +3 -0
  99. package/dist/events/ui-request.d.ts.map +1 -1
  100. package/dist/index.d.ts +54 -84
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +2135 -1470
  103. package/dist/protocols/protocol-v2/features.d.ts +13 -5
  104. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  105. package/dist/protocols/protocol-v2/index.d.ts +2 -2
  106. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  107. package/dist/protocols/protocol-v2/resources.d.ts +12 -7
  108. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  109. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +3 -1
  110. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
  111. package/dist/protocols/protocol-v2/walletSession.d.ts +3 -0
  112. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  113. package/dist/types/api/firmwareUpdatePlan.d.ts +2 -2
  114. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -1
  115. package/dist/types/api/protocolV2.d.ts +1 -1
  116. package/dist/types/api/protocolV2.d.ts.map +1 -1
  117. package/dist/types/device.d.ts.map +1 -1
  118. package/dist/types/settings.d.ts +3 -31
  119. package/dist/types/settings.d.ts.map +1 -1
  120. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  121. package/dist/utils/deviceFeaturesUtils.d.ts +3 -0
  122. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  123. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  124. package/dist/utils/deviceSettings.d.ts +1 -0
  125. package/dist/utils/deviceSettings.d.ts.map +1 -1
  126. package/dist/utils/index.d.ts +1 -1
  127. package/dist/utils/index.d.ts.map +1 -1
  128. package/dist/utils/patch.d.ts +1 -1
  129. package/dist/utils/patch.d.ts.map +1 -1
  130. package/dist/utils/pro2Nft.d.ts +7 -0
  131. package/dist/utils/pro2Nft.d.ts.map +1 -1
  132. package/package.json +6 -4
  133. package/src/api/BaseMethod.ts +8 -1
  134. package/src/api/CheckAllFirmwareRelease.ts +2 -4
  135. package/src/api/CheckBLEFirmwareRelease.ts +2 -2
  136. package/src/api/CheckBootloaderRelease.ts +3 -2
  137. package/src/api/CheckFirmwareRelease.ts +2 -2
  138. package/src/api/FirmwareUpdateV2.ts +12 -9
  139. package/src/api/FirmwareUpdateV3.ts +29 -19
  140. package/src/api/FirmwareUpdateV4.ts +918 -741
  141. package/src/api/OpenWalletSession.ts +39 -9
  142. package/src/api/PromptWebDeviceAccess.ts +2 -2
  143. package/src/api/SearchDevices.ts +6 -3
  144. package/src/api/UploadPortfolio.ts +9 -2
  145. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +2 -1
  146. package/src/api/device/DeviceChangePin.ts +4 -1
  147. package/src/api/device/DeviceRebootToBoardloader.ts +3 -1
  148. package/src/api/device/DeviceRebootToBootloader.ts +3 -1
  149. package/src/api/device/DeviceSettings.ts +34 -20
  150. package/src/api/device/DeviceVerify.ts +25 -0
  151. package/src/api/device/DeviceWipe.ts +4 -1
  152. package/src/api/firmware/FirmwareArtifactSource.ts +41 -14
  153. package/src/api/firmware/FirmwarePreparedResourceArchive.ts +16 -27
  154. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +9 -3
  155. package/src/api/firmware/FirmwareUpdatePlan.ts +37 -77
  156. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +10 -6
  157. package/src/api/firmware/protocolV2Release.ts +7 -2
  158. package/src/api/helpers/base64Data.ts +85 -0
  159. package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +1 -1
  160. package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +6 -28
  161. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +7 -1
  162. package/src/api/protocol-v2/DeviceInfoGet.ts +3 -3
  163. package/src/api/protocol-v2/DeviceReboot.ts +5 -0
  164. package/src/api/protocol-v2/DeviceUploadNft.ts +61 -9
  165. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +53 -19
  166. package/src/api/protocol-v2/helpers.ts +8 -0
  167. package/src/api/ton/TonSignMessage.ts +5 -3
  168. package/src/core/index.ts +264 -55
  169. package/src/data/messages/messages-protocol-v2.json +140 -13
  170. package/src/data-manager/DataManager.ts +46 -39
  171. package/src/data-manager/TransportManager.ts +3 -1
  172. package/src/device/Device.ts +210 -44
  173. package/src/device/DeviceCommands.ts +27 -18
  174. package/src/device/DeviceConnector.ts +17 -3
  175. package/src/device/DevicePool.ts +9 -2
  176. package/src/device/DeviceStateMapper.ts +30 -24
  177. package/src/deviceProfile/buildDeviceFeatures.ts +10 -5
  178. package/src/events/logBlockEvent.ts +14 -1
  179. package/src/events/ui-request.ts +3 -0
  180. package/src/index.ts +0 -10
  181. package/src/protocols/protocol-v2/features.ts +22 -5
  182. package/src/protocols/protocol-v2/index.ts +2 -0
  183. package/src/protocols/protocol-v2/resources.ts +110 -147
  184. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +13 -1
  185. package/src/protocols/protocol-v2/walletSession.ts +65 -22
  186. package/src/types/api/firmwareUpdate.ts +1 -1
  187. package/src/types/api/firmwareUpdatePlan.ts +2 -2
  188. package/src/types/api/protocolV2.ts +1 -1
  189. package/src/types/device.ts +2 -1
  190. package/src/types/settings.ts +6 -34
  191. package/src/utils/deviceFeaturesCompat.ts +9 -4
  192. package/src/utils/deviceFeaturesUtils.ts +4 -0
  193. package/src/utils/deviceInfoUtils.ts +9 -2
  194. package/src/utils/deviceSettings.ts +11 -4
  195. package/src/utils/index.ts +1 -0
  196. package/src/utils/pro2Nft.ts +31 -8
  197. package/__tests__/firmware-memory-host.test.ts +0 -126
  198. package/dist/api/firmware/FirmwareMemoryHost.d.ts +0 -22
  199. package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +0 -1
  200. package/src/api/firmware/FirmwareMemoryHost.ts +0 -143
@@ -29,6 +29,12 @@ const wasResumed = (session: unknown) =>
29
29
  'resumed' in session &&
30
30
  (session as { resumed?: unknown }).resumed === true;
31
31
 
32
+ const wasWalletStatusRefreshed = (session: unknown) =>
33
+ !!session &&
34
+ typeof session === 'object' &&
35
+ 'walletStatusRefreshed' in session &&
36
+ (session as { walletStatusRefreshed?: unknown }).walletStatusRefreshed === true;
37
+
32
38
  const requireHiddenWalletResponse = (session: { passphraseState?: string }) => {
33
39
  if (!session.passphraseState) {
34
40
  throw ERRORS.TypedError(
@@ -87,12 +93,20 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
87
93
  this.unlockPolicy = 'unlock-before-run';
88
94
  this.skipForceUpdateCheck = true;
89
95
  this.params = normalizeParams(this.payload as unknown as Record<string, unknown>);
96
+ this.protocolV2PreUnlockPinType =
97
+ this.params.mode === OpenWalletSessionMode.SelectHidden
98
+ ? DeviceSessionPinType.Any
99
+ : DeviceSessionPinType.Main;
90
100
  this.payload.useEmptyPassphrase = this.params.mode === OpenWalletSessionMode.Standard;
91
101
  }
92
102
 
93
103
  async run(): Promise<OpenWalletSessionPayload> {
94
104
  const isProtocolV2 = this.device.isProtocolV2();
95
- let state = await this.device.getDeviceState({ refreshSections: ['status'] });
105
+ const reusePreflightStatus =
106
+ isProtocolV2 && this.protocolV2UnlockContext?.preflightStatusRefreshed === true;
107
+ let state = reusePreflightStatus
108
+ ? await this.device.getDeviceState()
109
+ : await this.device.getDeviceState({ refreshSections: ['status'] });
96
110
  let currentDeviceId = state.identity.deviceId;
97
111
  const hasAuthoritativeProtocolV2WalletStatus = (candidate: typeof state) =>
98
112
  candidate.status.unlocked === true &&
@@ -115,14 +129,25 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
115
129
  currentDeviceId = state.identity.deviceId;
116
130
  return state;
117
131
  };
132
+ const resolveProtocolV2DeviceStateAfterSession = async (session: unknown) => {
133
+ if (!wasWalletStatusRefreshed(session)) {
134
+ return refreshProtocolV2DeviceState();
135
+ }
136
+ state = await this.device.getDeviceState();
137
+ currentDeviceId = state.identity.deviceId;
138
+ return state;
139
+ };
118
140
  const ensureProtocolV2WalletStatus = async () => {
119
141
  if (isProtocolV2 && !hasAuthoritativeProtocolV2WalletStatus(state)) {
120
- await this.device.unlockDevice(DeviceSessionPinType.Main, {
121
- source: 'unlock-coordinator',
122
- reason: 'device-locked',
123
- deviceOnly: true,
124
- method: 'openWalletSession',
125
- });
142
+ await this.device.unlockDevice(
143
+ this.protocolV2PreUnlockPinType ?? DeviceSessionPinType.Main,
144
+ {
145
+ source: 'unlock-coordinator',
146
+ reason: 'device-locked',
147
+ deviceOnly: true,
148
+ method: 'openWalletSession',
149
+ }
150
+ );
126
151
  requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState());
127
152
  }
128
153
  return state;
@@ -139,13 +164,16 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
139
164
  selectMainWalletBeforeRestore:
140
165
  !hasAuthoritativeProtocolV2WalletStatus(state) ||
141
166
  state.status.unlockedAttachPin === true,
167
+ mainPinSelected: this.protocolV2UnlockContext?.preflightMainPinSelected,
142
168
  })
143
169
  : await getPassphraseStateWithRefreshDeviceInfo(this.device, {
144
170
  onlyMainPin: true,
145
171
  initSession: this.payload.initSession,
146
172
  });
147
173
  const refreshedState = isProtocolV2
148
- ? requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState())
174
+ ? requireAuthoritativeProtocolV2WalletStatus(
175
+ await resolveProtocolV2DeviceStateAfterSession(session)
176
+ )
149
177
  : state;
150
178
  const deviceId = requireDeviceId();
151
179
  if (
@@ -233,7 +261,9 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
233
261
  })
234
262
  : await getPassphraseStateWithRefreshDeviceInfo(this.device, { initSession: true });
235
263
  const refreshedState = isProtocolV2
236
- ? requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState())
264
+ ? requireAuthoritativeProtocolV2WalletStatus(
265
+ await resolveProtocolV2DeviceStateAfterSession(session)
266
+ )
237
267
  : state;
238
268
  const deviceId = requireDeviceId();
239
269
  if (isProtocolV2 && refreshedState.status.passphraseProtection !== true) {
@@ -1,4 +1,4 @@
1
- import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
1
+ import { ERRORS, HardwareErrorCode, resolveOneKeyUsbDevicePath } from '@onekeyfe/hd-shared';
2
2
 
3
3
  import { BaseMethod } from './BaseMethod';
4
4
  import TransportManager from '../data-manager/TransportManager';
@@ -52,7 +52,7 @@ export default class PromptWebDeviceAccess extends BaseMethod {
52
52
 
53
53
  if (isWebUsbEnv) {
54
54
  const usbDevice = device as USBDevice;
55
- const path = usbDevice.serialNumber;
55
+ const path = resolveOneKeyUsbDevicePath(usbDevice);
56
56
  if (!path) {
57
57
  throw ERRORS.TypedError(HardwareErrorCode.WebDevicePromptAccessError);
58
58
  }
@@ -1,3 +1,5 @@
1
+ import { canonicalizePro2BleAdvertisementName } from '@onekeyfe/hd-shared';
2
+
1
3
  import { BaseMethod } from './BaseMethod';
2
4
  import TransportManager from '../data-manager/TransportManager';
3
5
  import { DataManager } from '../data-manager';
@@ -36,14 +38,15 @@ export default class SearchDevices extends BaseMethod {
36
38
  const lowerId = device.id?.toLowerCase();
37
39
  if (!seenIds.has(lowerId)) {
38
40
  seenIds.add(lowerId);
39
- const bleName =
41
+ const rawBleName =
40
42
  device.name ?? (device as unknown as { localName?: string }).localName ?? '';
43
+ const bleName = canonicalizePro2BleAdvertisementName(rawBleName);
41
44
  devices.push({
42
45
  ...device,
43
46
  connectId: device.id,
44
47
  serialNo: null,
45
- // Legacy BLE discovery identifier. The physical serial number is unavailable here.
46
- uuid: device.id,
48
+ // BLE discovery cannot provide a physical-device identity before initialization.
49
+ uuid: '',
47
50
  deviceId: null,
48
51
  name: bleName || device.name,
49
52
  deviceType: getDeviceTypeByBleName(bleName),
@@ -1,21 +1,28 @@
1
1
  import { createDeviceNotSupportMethodError } from '@onekeyfe/hd-shared';
2
2
 
3
3
  import { supportsProtocolV2Message } from '../protocols/protocol-v2/features';
4
+ import { decodeCanonicalBase64 } from './helpers/base64Data';
4
5
  import FileWrite from './FileWrite';
5
6
 
6
7
  export type UploadPortfolioParams = {
7
- packageBytes: ArrayBuffer | Uint8Array | Blob;
8
+ packageBase64: string;
8
9
  timeoutMs?: number | string;
9
10
  };
10
11
 
11
12
  const PORTFOLIO_PENDING_PATH = 'vol1:/portfolio/portfolio.okpkg.pending';
12
13
  const PORTFOLIO_CHUNK_SIZE = 2048;
14
+ const PORTFOLIO_PACKAGE_MAX_BYTES = 128 * 1024;
13
15
  const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
14
16
  const PORTFOLIO_UPDATE_MESSAGE_TYPE = 61400;
15
17
 
16
18
  export default class UploadPortfolio extends FileWrite {
17
19
  init() {
18
- const { packageBytes, timeoutMs } = this.payload as UploadPortfolioParams;
20
+ const { packageBase64, timeoutMs } = this.payload as UploadPortfolioParams;
21
+ const packageBytes = decodeCanonicalBase64({
22
+ value: packageBase64,
23
+ parameterName: 'packageBase64',
24
+ maxBytes: PORTFOLIO_PACKAGE_MAX_BYTES,
25
+ });
19
26
  this.payload = {
20
27
  ...this.payload,
21
28
  path: PORTFOLIO_PENDING_PATH,
@@ -400,7 +400,8 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
400
400
  this.payload.passphraseState,
401
401
  useEmptyPassphrase,
402
402
  this.payload.skipPassphraseCheck,
403
- deriveCardano
403
+ deriveCardano,
404
+ this.protocolV2UnlockContext?.preflightMainPinSelected
404
405
  );
405
406
  if (!passphraseStateSafety) {
406
407
  throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
@@ -1,4 +1,4 @@
1
- import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
1
+ import { DeviceSessionPinType, DeviceSettingsPage } from '@onekeyfe/hd-transport';
2
2
 
3
3
  import { BaseMethod } from '../BaseMethod';
4
4
  import { validateParams } from '../helpers/paramsValidator';
@@ -13,6 +13,9 @@ export default class DeviceChangePin extends BaseMethod<ChangePin> {
13
13
 
14
14
  init() {
15
15
  this.unlockPolicy = 'unlock-before-run';
16
+ // Protocol V2 device-management actions are not wallet-scoped, so either
17
+ // the main PIN or an Attach PIN may authorize them.
18
+ this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
16
19
  this.useDevicePassphraseState = false;
17
20
 
18
21
  // check payload
@@ -1,4 +1,4 @@
1
- import { DeviceRebootType } from '@onekeyfe/hd-transport';
1
+ import { DeviceRebootType, DeviceSessionPinType } from '@onekeyfe/hd-transport';
2
2
 
3
3
  import { BaseMethod } from '../BaseMethod';
4
4
 
@@ -13,6 +13,8 @@ export default class DeviceRebootToBoardloader extends BaseMethod<RebootToBoardl
13
13
  init() {
14
14
  this.useDevicePassphraseState = false;
15
15
  this.skipForceUpdateCheck = true;
16
+ this.unlockPolicy = 'unlock-before-run';
17
+ this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
16
18
  }
17
19
 
18
20
  getVersionRange() {
@@ -1,4 +1,4 @@
1
- import { DeviceRebootType } from '@onekeyfe/hd-transport';
1
+ import { DeviceRebootType, DeviceSessionPinType } from '@onekeyfe/hd-transport';
2
2
 
3
3
  import { BaseMethod } from '../BaseMethod';
4
4
 
@@ -13,6 +13,8 @@ export default class DeviceRebootToBootloader extends BaseMethod<RebootToBootloa
13
13
  init() {
14
14
  this.useDevicePassphraseState = false;
15
15
  this.skipForceUpdateCheck = true;
16
+ this.unlockPolicy = 'unlock-before-run';
17
+ this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
16
18
  }
17
19
 
18
20
  getVersionRange() {
@@ -1,5 +1,5 @@
1
1
  import { HardwareErrorCode, TypedError } from '@onekeyfe/hd-shared';
2
- import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
2
+ import { DeviceSessionPinType, DeviceSettingsPage } from '@onekeyfe/hd-transport';
3
3
 
4
4
  import { BaseMethod } from '../BaseMethod';
5
5
  import { invalidParameter } from '../helpers/filesystemValidation';
@@ -10,6 +10,7 @@ import {
10
10
  } from '../../device/DeviceStateMapper';
11
11
  import { getProtocolV2SettingsBehavior } from '../../protocols/protocol-v2/settingsUnlockPolicy';
12
12
  import {
13
+ DEVICE_SETTINGS_NEVER_TIMEOUT_MS,
13
14
  DEVICE_SETTINGS_V1_ONLY_FIELDS,
14
15
  DEVICE_SETTINGS_V2_ONLY_FIELDS,
15
16
  LANGUAGE_LABELS,
@@ -30,6 +31,9 @@ const assertSettingsSupported = (
30
31
  }
31
32
  };
32
33
 
34
+ const normalizeProtocolV1DelayMs = (value: number | undefined) =>
35
+ value === 0 ? DEVICE_SETTINGS_NEVER_TIMEOUT_MS : value;
36
+
33
37
  const assertProtocolV2SettingValues = (
34
38
  payload: DeviceSettingsParams,
35
39
  capabilities: ReturnType<typeof getDeviceSettingsCapabilities>
@@ -145,6 +149,8 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
145
149
  })
146
150
  : getProtocolV2SettingsBehavior({ kind: 'page', page });
147
151
  this.unlockPolicy = behavior.unlockPolicy;
152
+ this.protocolV2PreUnlockPinType =
153
+ behavior.unlockPolicy === 'unlock-before-run' ? DeviceSessionPinType.Any : undefined;
148
154
  this.protocolV2UiInteraction = behavior.uiInteraction;
149
155
  }
150
156
 
@@ -162,8 +168,7 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
162
168
  async run() {
163
169
  try {
164
170
  if (this.device.isProtocolV2()) {
165
- const refreshStatusAndSettings = () =>
166
- this.device.getDeviceState({ refreshSections: ['status', 'settings'] });
171
+ const refreshStatusAndSettings = () => this.device.refreshProtocolV2SettingsAfterMutation();
167
172
  assertSettingsSupported(this.payload, DEVICE_SETTINGS_V1_ONLY_FIELDS, 'Protocol V2');
168
173
  const capabilities = getDeviceSettingsCapabilities(
169
174
  this.device.getCurrentDeviceType(),
@@ -195,20 +200,20 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
195
200
  const res = await this.device.commands.typedCall('DeviceSettingsPageShow', 'Success', {
196
201
  page: DeviceSettingsPage.DevicePassphrase,
197
202
  });
198
- const updated = await refreshStatusAndSettings();
199
- const lockedAfterDisabling =
200
- requestedPassphrase === false &&
201
- current.status.unlocked === true &&
202
- updated.status.unlocked === false;
203
- if (
204
- updated.status.passphraseProtection !== requestedPassphrase &&
205
- !lockedAfterDisabling
206
- ) {
207
- throw TypedError(
208
- HardwareErrorCode.RuntimeError,
209
- 'Protocol V2 passphrase setting did not reach the requested value.'
210
- );
211
- }
203
+ // A successful page response confirms the device-side toggle. When an Attach PIN
204
+ // session disables passphrase protection, the firmware locks immediately and omits
205
+ // private passphrase fields from subsequent reads, so persist the confirmed value
206
+ // before refreshing the remaining device state.
207
+ this.device.updateState(
208
+ {
209
+ status: {
210
+ passphraseProtection: requestedPassphrase,
211
+ ...(requestedPassphrase === false ? { unlockedAttachPin: false } : undefined),
212
+ },
213
+ },
214
+ 'settings-write'
215
+ );
216
+ await refreshStatusAndSettings();
212
217
  return res.message;
213
218
  }
214
219
  if (requestedAirgap !== undefined) {
@@ -253,10 +258,19 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
253
258
  );
254
259
  }
255
260
 
256
- const res = await this.device.commands.typedCall('ApplySettings', 'Success', {
261
+ // Early SDK 1.2 alphas exposed 0 as Protocol V1 "Never". Normalize at the
262
+ // protocol boundary so existing consumers send the firmware-supported value.
263
+ const protocolV1Params = {
257
264
  ...this.params,
258
- });
259
- this.device.updateState(mapApplySettingsToState(this.params), 'settings-write');
265
+ auto_lock_delay_ms: normalizeProtocolV1DelayMs(this.params.auto_lock_delay_ms),
266
+ auto_shutdown_delay_ms: normalizeProtocolV1DelayMs(this.params.auto_shutdown_delay_ms),
267
+ };
268
+ const res = await this.device.commands.typedCall(
269
+ 'ApplySettings',
270
+ 'Success',
271
+ protocolV1Params
272
+ );
273
+ this.device.updateState(mapApplySettingsToState(protocolV1Params), 'settings-write');
260
274
  return res.message;
261
275
  } catch (error) {
262
276
  if (error.message?.toLowerCase().includes('no setting provided')) {
@@ -1,5 +1,6 @@
1
1
  import { sha256 } from '@noble/hashes/sha256';
2
2
  import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
3
+ import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
3
4
  import { bytesToHex } from '@noble/hashes/utils';
4
5
 
5
6
  import { formatAnyHex } from '../helpers/hexUtils';
@@ -11,7 +12,15 @@ import type { BixinVerifyDeviceRequest } from '@onekeyfe/hd-transport';
11
12
  import type { DeviceVerifySignature } from '../../types';
12
13
 
13
14
  export default class DeviceVerify extends BaseMethod<BixinVerifyDeviceRequest> {
15
+ getSupportedProtocols() {
16
+ return ['V1', 'V2'] as const;
17
+ }
18
+
14
19
  init() {
20
+ this.unlockPolicy = 'unlock-before-run';
21
+ // Protocol V2 device-management actions are not wallet-scoped, so either
22
+ // the main PIN or an Attach PIN may authorize them.
23
+ this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
15
24
  this.useDevicePassphraseState = false;
16
25
 
17
26
  // check payload
@@ -38,6 +47,22 @@ export default class DeviceVerify extends BaseMethod<BixinVerifyDeviceRequest> {
38
47
  }
39
48
  );
40
49
  response = res.message;
50
+ } else if (this.device.isProtocolV2()) {
51
+ const signatureRes = await this.device.commands.typedCall(
52
+ 'DeviceCertificateSign',
53
+ 'DeviceCertificateSignature',
54
+ {
55
+ data: this.params.data,
56
+ }
57
+ );
58
+ const certRes = await this.device.commands.typedCall(
59
+ 'DeviceCertificateRead',
60
+ 'DeviceCertificate'
61
+ );
62
+ response = {
63
+ cert: certRes.message.cert_and_pubkey,
64
+ signature: signatureRes.message.data,
65
+ };
41
66
  } else {
42
67
  const signatureRes = await this.device.commands.typedCall(
43
68
  'SESignMessage',
@@ -1,4 +1,4 @@
1
- import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
1
+ import { DeviceSessionPinType, DeviceSettingsPage } from '@onekeyfe/hd-transport';
2
2
 
3
3
  import { BaseMethod } from '../BaseMethod';
4
4
 
@@ -11,6 +11,9 @@ export default class DeviceWipe extends BaseMethod<WipeDevice> {
11
11
 
12
12
  init() {
13
13
  this.unlockPolicy = 'unlock-before-run';
14
+ // Protocol V2 device-management actions are not wallet-scoped, so either
15
+ // the main PIN or an Attach PIN may authorize them.
16
+ this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
14
17
  this.useDevicePassphraseState = false;
15
18
  this.protocolV2UiInteraction = {
16
19
  request: 'button',
@@ -1,4 +1,6 @@
1
1
  import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+ import { sha256 } from '@noble/hashes/sha256';
3
+ import { bytesToHex } from '@noble/hashes/utils';
2
4
 
3
5
  import type {
4
6
  FirmwareArtifactReader,
@@ -188,6 +190,43 @@ class ReaderFirmwareByteSource implements FirmwareByteSource {
188
190
  }
189
191
  }
190
192
 
193
+ export const readFirmwareByteSourceFully = async (
194
+ source: FirmwareByteSource
195
+ ): Promise<ArrayBuffer> => {
196
+ const output = new Uint8Array(source.size);
197
+ let offset = 0;
198
+ while (offset < source.size) {
199
+ const length = Math.min(MAX_FIRMWARE_ARTIFACT_READ_BYTES, source.size - offset);
200
+ output.set(new Uint8Array(await source.readAt(offset, length)), offset);
201
+ offset += length;
202
+ }
203
+ return output.buffer;
204
+ };
205
+
206
+ export const readVerifiedFirmwareArtifact = async ({
207
+ artifact,
208
+ reader,
209
+ }: {
210
+ artifact: FirmwareArtifactReference;
211
+ reader: FirmwareArtifactReader;
212
+ }): Promise<ArrayBuffer> => {
213
+ assertFirmwareArtifactReference(artifact);
214
+ const source = await ReaderFirmwareByteSource.open(artifact, reader);
215
+ try {
216
+ const binary = await readFirmwareByteSourceFully(source);
217
+ const actualSha256 = bytesToHex(sha256(new Uint8Array(binary)));
218
+ if (actualSha256 !== artifact.sha256.toLowerCase()) {
219
+ return artifactError(
220
+ 'FirmwareArtifactReceiptMismatch',
221
+ 'Firmware artifact bytes do not match the approved SHA-256 receipt'
222
+ );
223
+ }
224
+ return binary;
225
+ } finally {
226
+ await source.close().catch(() => undefined);
227
+ }
228
+ };
229
+
191
230
  export const openFirmwareByteSource = async ({
192
231
  binary,
193
232
  artifact,
@@ -216,20 +255,8 @@ export const openFirmwareByteSource = async ({
216
255
  'Firmware artifact reader is required for artifactRef input'
217
256
  );
218
257
  }
219
- return ReaderFirmwareByteSource.open(artifact, reader);
220
- };
221
-
222
- export const readFirmwareByteSourceFully = async (
223
- source: FirmwareByteSource
224
- ): Promise<ArrayBuffer> => {
225
- const output = new Uint8Array(source.size);
226
- let offset = 0;
227
- while (offset < source.size) {
228
- const length = Math.min(MAX_FIRMWARE_ARTIFACT_READ_BYTES, source.size - offset);
229
- output.set(new Uint8Array(await source.readAt(offset, length)), offset);
230
- offset += length;
231
- }
232
- return output.buffer;
258
+ const verifiedBinary = await readVerifiedFirmwareArtifact({ artifact, reader });
259
+ return createMemoryFirmwareByteSource(verifiedBinary);
233
260
  };
234
261
 
235
262
  export const writeFirmwareByteSource = async ({
@@ -3,7 +3,7 @@ import { bytesToHex } from '@noble/hashes/utils';
3
3
  import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
4
4
  import JSZip from 'jszip';
5
5
 
6
- import { openFirmwareByteSource, readFirmwareByteSourceFully } from './FirmwareArtifactSource';
6
+ import { readVerifiedFirmwareArtifact } from './FirmwareArtifactSource';
7
7
  import { getFirmwareUpdateResourceName } from './FirmwareUpdatePreparedPlan';
8
8
 
9
9
  import type { FirmwareArtifactReader } from '../../types/api/firmwareUpdate';
@@ -26,7 +26,10 @@ export type VerifiedPreparedResourceEntry = {
26
26
 
27
27
  const resourceArchiveError = (
28
28
  message: string,
29
- firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' | 'FirmwareArtifactReceiptMismatch'
29
+ firmwareUpdateCode:
30
+ | 'FirmwareArtifactsNotPrepared'
31
+ | 'FirmwareArtifactReceiptMismatch'
32
+ | 'FirmwareArtifactReaderInvalid'
30
33
  ): never => {
31
34
  throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, message, { firmwareUpdateCode });
32
35
  };
@@ -73,6 +76,12 @@ export const readVerifiedPreparedResourceArchive = async ({
73
76
  }
74
77
  return [];
75
78
  }
79
+ if (!reader) {
80
+ return resourceArchiveError(
81
+ 'Firmware artifact reader is required for prepared resource archives',
82
+ 'FirmwareArtifactReaderInvalid'
83
+ );
84
+ }
76
85
  if (resourceArtifacts.length !== 1) {
77
86
  return resourceArchiveError(
78
87
  'Firmware prepared plan must contain exactly one materialized resource ZIP',
@@ -92,37 +101,17 @@ export const readVerifiedPreparedResourceArchive = async ({
92
101
  'FirmwareArtifactsNotPrepared'
93
102
  );
94
103
  }
95
- const archiveSource = await openFirmwareByteSource({
96
- artifact: archiveArtifact.artifact,
97
- reader,
98
- });
99
- if (!archiveSource) {
100
- return resourceArchiveError(
101
- 'Firmware prepared resource ZIP is unavailable',
102
- 'FirmwareArtifactsNotPrepared'
103
- );
104
- }
105
- if (archiveSource.size > PREPARED_RESOURCE_ARCHIVE_MAX_BYTES) {
106
- await archiveSource.close().catch(() => undefined);
104
+ if (archiveArtifact.artifact.size > PREPARED_RESOURCE_ARCHIVE_MAX_BYTES) {
107
105
  return resourceArchiveError(
108
106
  'Firmware prepared resource ZIP exceeds the archive size limit',
109
107
  'FirmwareArtifactsNotPrepared'
110
108
  );
111
109
  }
112
110
 
113
- let archiveBinary: ArrayBuffer;
114
- try {
115
- archiveBinary = await readFirmwareByteSourceFully(archiveSource);
116
- } finally {
117
- await archiveSource.close().catch(() => undefined);
118
- }
119
- const archiveDigest = bytesToHex(sha256(new Uint8Array(archiveBinary)));
120
- if (archiveDigest !== archiveArtifact.artifact.sha256.toLowerCase()) {
121
- return resourceArchiveError(
122
- 'Firmware prepared resource ZIP does not match its approved receipt',
123
- 'FirmwareArtifactReceiptMismatch'
124
- );
125
- }
111
+ const archiveBinary = await readVerifiedFirmwareArtifact({
112
+ artifact: archiveArtifact.artifact,
113
+ reader,
114
+ });
126
115
 
127
116
  let zip: JSZip;
128
117
  try {
@@ -41,9 +41,15 @@ const FIRMWARE_UPDATE_CONFIRM = 'Firmware install confirmed';
41
41
  */
42
42
  export const BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS = 5000;
43
43
 
44
- type FirmwareTransferMetrics = Pick<
44
+ type FirmwareProgressMetadata = Pick<
45
45
  FirmwareProgress['payload'],
46
- 'transferredBytes' | 'totalBytes' | 'rateBytesPerSecond' | 'elapsedMs'
46
+ | 'transferredBytes'
47
+ | 'totalBytes'
48
+ | 'rateBytesPerSecond'
49
+ | 'elapsedMs'
50
+ | 'installTargetId'
51
+ | 'installPhase'
52
+ | 'installPhaseProgress'
47
53
  >;
48
54
 
49
55
  const isDeviceDisconnectedError = (error: unknown) => {
@@ -103,7 +109,7 @@ export class FirmwareUpdateBaseMethod<Params> extends BaseMethod<Params> {
103
109
  postProgressMessage = (
104
110
  progress: number,
105
111
  progressType: IFirmwareUpdateProgressType,
106
- metrics?: FirmwareTransferMetrics
112
+ metrics?: FirmwareProgressMetadata
107
113
  ) => {
108
114
  this.postMessage(
109
115
  createUiMessage(UI_REQUEST.FIRMWARE_PROGRESS, {