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

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 (238) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +0 -78
  2. package/__tests__/DeviceEventRegistration.test.ts +0 -6
  3. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +3 -111
  4. package/__tests__/connectSettings.test.ts +47 -5
  5. package/__tests__/device-initialize-timeout.test.ts +56 -0
  6. package/__tests__/device-lifecycle-events.test.ts +3 -105
  7. package/__tests__/firmware-update/check-all-firmware-release.test.ts +175 -0
  8. package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
  9. package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
  10. package/__tests__/firmware-update/firmware-preparation-error.test.ts +27 -0
  11. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +200 -0
  12. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
  13. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +294 -0
  14. package/__tests__/firmware-update/firmware-update-plan.test.ts +593 -0
  15. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +213 -0
  16. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +59 -31
  17. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +58 -0
  18. package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
  19. package/__tests__/get-device-state.test.ts +12 -132
  20. package/__tests__/homescreen.test.ts +1 -41
  21. package/__tests__/logBlockEvent.test.ts +1 -1
  22. package/__tests__/method-protocol-support.test.ts +0 -19
  23. package/__tests__/open-wallet-session.test.ts +33 -446
  24. package/__tests__/protocol-v2-unlock-policy.test.ts +35 -196
  25. package/__tests__/protocol-v2.test.ts +1032 -1056
  26. package/__tests__/protocolV2UiInteraction.test.ts +0 -63
  27. package/__tests__/public-pro2-api-boundary.test.ts +0 -1
  28. package/__tests__/search-devices.test.ts +3 -12
  29. package/dist/api/BaseMethod.d.ts +1 -3
  30. package/dist/api/BaseMethod.d.ts.map +1 -1
  31. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  32. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  33. package/dist/api/FirmwareUpdateV2.d.ts +11 -0
  34. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  35. package/dist/api/FirmwareUpdateV3.d.ts +7 -3
  36. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  37. package/dist/api/FirmwareUpdateV4.d.ts +25 -9
  38. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  39. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  40. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  41. package/dist/api/SearchDevices.d.ts.map +1 -1
  42. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  43. package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
  44. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  45. package/dist/api/firmware/FirmwareArtifactSource.d.ts +25 -0
  46. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
  47. package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
  48. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
  49. package/dist/api/firmware/FirmwarePreparationError.d.ts +3 -0
  50. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +1 -0
  51. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +9 -0
  52. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  53. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
  54. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
  55. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +25 -0
  56. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
  57. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +32 -0
  58. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
  59. package/dist/api/firmware/getBinary.d.ts +7 -0
  60. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  61. package/dist/api/firmware/progressThrottle.d.ts +3 -0
  62. package/dist/api/firmware/progressThrottle.d.ts.map +1 -0
  63. package/dist/api/firmware/updateBootloader.d.ts +2 -0
  64. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  65. package/dist/api/firmware/uploadFirmware.d.ts +9 -1
  66. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  67. package/dist/api/helpers/protocolV2FileWrite.d.ts +0 -2
  68. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  69. package/dist/api/index.d.ts +0 -2
  70. package/dist/api/index.d.ts.map +1 -1
  71. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  72. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  73. package/dist/core/deviceEventRegistration.d.ts +0 -2
  74. package/dist/core/deviceEventRegistration.d.ts.map +1 -1
  75. package/dist/core/index.d.ts.map +1 -1
  76. package/dist/data-manager/DataManager.d.ts +2 -4
  77. package/dist/data-manager/DataManager.d.ts.map +1 -1
  78. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  79. package/dist/device/Device.d.ts +6 -29
  80. package/dist/device/Device.d.ts.map +1 -1
  81. package/dist/device/DeviceCommands.d.ts +4 -4
  82. package/dist/device/DevicePool.d.ts.map +1 -1
  83. package/dist/events/device.d.ts +0 -4
  84. package/dist/events/device.d.ts.map +1 -1
  85. package/dist/events/logBlockEvent.d.ts.map +1 -1
  86. package/dist/events/ui-promise.d.ts +2 -4
  87. package/dist/events/ui-promise.d.ts.map +1 -1
  88. package/dist/events/ui-request.d.ts +2 -31
  89. package/dist/events/ui-request.d.ts.map +1 -1
  90. package/dist/events/ui-response.d.ts +2 -10
  91. package/dist/events/ui-response.d.ts.map +1 -1
  92. package/dist/index.d.ts +230 -203
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +3469 -2419
  95. package/dist/inject.d.ts +1 -6
  96. package/dist/inject.d.ts.map +1 -1
  97. package/dist/lowLevelInject.d.ts.map +1 -1
  98. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  99. package/dist/protocols/protocol-v2/lockedError.d.ts +2 -0
  100. package/dist/protocols/protocol-v2/lockedError.d.ts.map +1 -0
  101. package/dist/protocols/protocol-v2/uiInteraction.d.ts +4 -7
  102. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  103. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +3 -1
  104. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
  105. package/dist/protocols/protocol-v2/unlockRetry.d.ts +10 -0
  106. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -0
  107. package/dist/protocols/protocol-v2/walletSession.d.ts +1 -4
  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 +4 -1
  111. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  112. package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
  113. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  114. package/dist/types/api/export.d.ts +4 -1
  115. package/dist/types/api/export.d.ts.map +1 -1
  116. package/dist/types/api/firmwareUpdate.d.ts +70 -2
  117. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  118. package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
  119. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
  120. package/dist/types/api/firmwareUpdatePlan.d.ts +28 -0
  121. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
  122. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
  123. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
  124. package/dist/types/api/index.d.ts +10 -7
  125. package/dist/types/api/index.d.ts.map +1 -1
  126. package/dist/types/api/protocolV2.d.ts +0 -3
  127. package/dist/types/api/protocolV2.d.ts.map +1 -1
  128. package/dist/types/params.d.ts +0 -1
  129. package/dist/types/params.d.ts.map +1 -1
  130. package/dist/types/settings.d.ts +13 -22
  131. package/dist/types/settings.d.ts.map +1 -1
  132. package/dist/utils/deviceFeaturesCompat.d.ts +1 -0
  133. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  134. package/dist/utils/deviceFeaturesUtils.d.ts +0 -2
  135. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  136. package/dist/utils/homescreen.d.ts +0 -4
  137. package/dist/utils/homescreen.d.ts.map +1 -1
  138. package/dist/utils/index.d.ts +1 -1
  139. package/dist/utils/index.d.ts.map +1 -1
  140. package/dist/utils/patch.d.ts +1 -1
  141. package/dist/utils/patch.d.ts.map +1 -1
  142. package/dist/utils/pro2Wallpaper.d.ts +0 -10
  143. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  144. package/package.json +4 -4
  145. package/src/api/BaseMethod.ts +10 -8
  146. package/src/api/CheckAllFirmwareRelease.ts +53 -41
  147. package/src/api/FirmwareUpdate.ts +15 -7
  148. package/src/api/FirmwareUpdateV2.ts +316 -126
  149. package/src/api/FirmwareUpdateV3.ts +265 -63
  150. package/src/api/FirmwareUpdateV4.ts +937 -430
  151. package/src/api/GetPassphraseState.ts +1 -7
  152. package/src/api/OpenWalletSession.ts +17 -54
  153. package/src/api/SearchDevices.ts +1 -3
  154. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +11 -32
  155. package/src/api/device/DeviceUnlock.ts +1 -1
  156. package/src/api/device/DeviceUpdateBootloader.ts +122 -6
  157. package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
  158. package/src/api/firmware/FirmwareHostBinding.ts +100 -0
  159. package/src/api/firmware/FirmwarePreparationError.ts +12 -0
  160. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +83 -16
  161. package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
  162. package/src/api/firmware/FirmwareUpdatePlan.ts +772 -0
  163. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +415 -0
  164. package/src/api/firmware/progressThrottle.ts +44 -0
  165. package/src/api/firmware/updateBootloader.ts +19 -4
  166. package/src/api/firmware/uploadFirmware.ts +144 -39
  167. package/src/api/helpers/protocolV2FileWrite.ts +4 -11
  168. package/src/api/index.ts +0 -2
  169. package/src/api/protocol-v2/ProtocolInfoRequest.ts +1 -3
  170. package/src/core/deviceEventRegistration.ts +0 -4
  171. package/src/core/index.ts +83 -167
  172. package/src/data/messages/messages-protocol-v2.json +0 -25
  173. package/src/data-manager/DataManager.ts +38 -67
  174. package/src/data-manager/connectSettings.ts +11 -0
  175. package/src/device/Device.ts +42 -257
  176. package/src/device/DevicePool.ts +2 -7
  177. package/src/events/device.ts +0 -4
  178. package/src/events/logBlockEvent.ts +0 -1
  179. package/src/events/ui-promise.ts +2 -4
  180. package/src/events/ui-request.ts +2 -36
  181. package/src/events/ui-response.ts +2 -12
  182. package/src/index.ts +5 -0
  183. package/src/inject.ts +24 -60
  184. package/src/lowLevelInject.ts +8 -7
  185. package/src/protocols/protocol-v2/features.ts +2 -9
  186. package/src/protocols/protocol-v2/lockedError.ts +10 -0
  187. package/src/protocols/protocol-v2/uiInteraction.ts +6 -46
  188. package/src/protocols/protocol-v2/unlockPolicy.ts +105 -1
  189. package/src/protocols/protocol-v2/unlockRetry.ts +109 -0
  190. package/src/protocols/protocol-v2/walletSession.ts +30 -169
  191. package/src/topLevelInject.ts +8 -7
  192. package/src/types/api/checkAllFirmwareRelease.ts +4 -1
  193. package/src/types/api/deviceUpdateBootloader.ts +6 -0
  194. package/src/types/api/export.ts +18 -0
  195. package/src/types/api/firmwareUpdate.ts +76 -3
  196. package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
  197. package/src/types/api/firmwareUpdatePlan.ts +38 -0
  198. package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
  199. package/src/types/api/index.ts +14 -13
  200. package/src/types/api/protocolV2.ts +0 -13
  201. package/src/types/params.ts +1 -7
  202. package/src/types/settings.ts +13 -42
  203. package/src/utils/deviceFeaturesCompat.ts +6 -0
  204. package/src/utils/deviceFeaturesUtils.ts +0 -4
  205. package/src/utils/homescreen.ts +6 -32
  206. package/src/utils/index.ts +1 -6
  207. package/src/utils/pro2Wallpaper.ts +8 -35
  208. package/__tests__/deviceUploadNft.test.ts +0 -293
  209. package/__tests__/pro2Nft.test.ts +0 -108
  210. package/__tests__/protocol-binding.test.ts +0 -89
  211. package/__tests__/protocol-v2-resources.test.ts +0 -284
  212. package/__tests__/protocol-v2-ui-lifecycle.test.ts +0 -91
  213. package/__tests__/uiProgressThrottle.test.ts +0 -74
  214. package/__tests__/uiPromiseRegistry.test.ts +0 -86
  215. package/dist/api/DetectDeviceConnectProtocol.d.ts +0 -7
  216. package/dist/api/DetectDeviceConnectProtocol.d.ts.map +0 -1
  217. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +0 -31
  218. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +0 -1
  219. package/dist/core/uiPromiseRegistry.d.ts +0 -4
  220. package/dist/core/uiPromiseRegistry.d.ts.map +0 -1
  221. package/dist/protocols/protocol-v2/resources.d.ts +0 -30
  222. package/dist/protocols/protocol-v2/resources.d.ts.map +0 -1
  223. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +0 -22
  224. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +0 -1
  225. package/dist/types/api/detectDeviceConnectProtocol.d.ts +0 -4
  226. package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +0 -1
  227. package/dist/utils/pro2Nft.d.ts +0 -31
  228. package/dist/utils/pro2Nft.d.ts.map +0 -1
  229. package/dist/utils/uiProgressThrottle.d.ts +0 -3
  230. package/dist/utils/uiProgressThrottle.d.ts.map +0 -1
  231. package/src/api/DetectDeviceConnectProtocol.ts +0 -18
  232. package/src/api/protocol-v2/DeviceUploadNft.ts +0 -189
  233. package/src/core/uiPromiseRegistry.ts +0 -41
  234. package/src/protocols/protocol-v2/resources.ts +0 -359
  235. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +0 -124
  236. package/src/types/api/detectDeviceConnectProtocol.ts +0 -7
  237. package/src/utils/pro2Nft.ts +0 -142
  238. package/src/utils/uiProgressThrottle.ts +0 -63
@@ -0,0 +1,415 @@
1
+ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+
3
+ import { assertFirmwareArtifactReference } from './FirmwareArtifactSource';
4
+ import {
5
+ FIRMWARE_UPDATE_PLAN_ROLES,
6
+ FIRMWARE_UPDATE_PLAN_TARGETS,
7
+ assertFirmwareUpdatePlan,
8
+ digestFirmwareUpdateContract,
9
+ } from './FirmwareUpdatePlan';
10
+
11
+ import type {
12
+ FirmwareUpdatePreparedArtifact,
13
+ FirmwareUpdatePreparedArtifactInput,
14
+ FirmwareUpdatePreparedEntry,
15
+ FirmwareUpdatePreparedPlan,
16
+ } from '../../types/api/firmwareUpdatePreparedPlan';
17
+ import type { FirmwareArtifactReference } from '../../types/api/firmwareUpdate';
18
+ import type { FirmwareUpdatePlan } from '../../types/api/firmwareUpdatePlan';
19
+
20
+ const preparedPlanError = (message: string): never => {
21
+ throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, message, {
22
+ firmwareUpdateCode: 'FirmwarePreparedPlanInvalid',
23
+ });
24
+ };
25
+
26
+ const digestPreparedPlan = (plan: Omit<FirmwareUpdatePreparedPlan, 'preparedPlanDigest'>): string =>
27
+ digestFirmwareUpdateContract(plan);
28
+
29
+ const assertOpaqueLeaseRef = (value: unknown): string => {
30
+ if (
31
+ typeof value !== 'string' ||
32
+ value.length === 0 ||
33
+ value.length > 160 ||
34
+ value.includes('/') ||
35
+ value.includes('\\')
36
+ ) {
37
+ return preparedPlanError('Firmware prepared plan leaseRef must be opaque');
38
+ }
39
+ return value;
40
+ };
41
+
42
+ export const getFirmwareUpdateResourceName = (value: unknown): string => {
43
+ const normalized = typeof value === 'string' ? value.normalize('NFC') : undefined;
44
+ const parts = normalized?.split('/');
45
+ const resourceName = parts?.at(-1);
46
+ if (
47
+ typeof value !== 'string' ||
48
+ value.length === 0 ||
49
+ value.length > 512 ||
50
+ value !== normalized ||
51
+ value.startsWith('/') ||
52
+ value.includes('\\') ||
53
+ value.includes(':') ||
54
+ [...value].some(char => {
55
+ const code = char.charCodeAt(0);
56
+ return code <= 0x1f || code === 0x7f;
57
+ }) ||
58
+ !parts?.every(
59
+ part =>
60
+ part.length > 0 &&
61
+ part !== '.' &&
62
+ part !== '..' &&
63
+ !part.endsWith('.') &&
64
+ !part.endsWith(' ')
65
+ ) ||
66
+ !resourceName ||
67
+ resourceName.length > 128
68
+ ) {
69
+ return preparedPlanError('Firmware prepared plan entry name is invalid');
70
+ }
71
+ return resourceName;
72
+ };
73
+
74
+ const assertPreparedEntry = (value: unknown): FirmwareUpdatePreparedEntry => {
75
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
76
+ return preparedPlanError('Firmware prepared plan entry must be an object');
77
+ }
78
+ const entry = value as Record<string, unknown>;
79
+ if (
80
+ Object.keys(entry).length !== 2 ||
81
+ !Object.prototype.hasOwnProperty.call(entry, 'entryName') ||
82
+ !Object.prototype.hasOwnProperty.call(entry, 'artifact')
83
+ ) {
84
+ return preparedPlanError('Firmware prepared plan entry fields are invalid');
85
+ }
86
+ getFirmwareUpdateResourceName(entry.entryName);
87
+ assertFirmwareArtifactReference(entry.artifact as FirmwareUpdatePreparedEntry['artifact']);
88
+ return value as FirmwareUpdatePreparedEntry;
89
+ };
90
+
91
+ const assertPreparedArtifacts = ({
92
+ plan,
93
+ artifacts,
94
+ }: {
95
+ plan: FirmwareUpdatePlan;
96
+ artifacts: readonly FirmwareUpdatePreparedArtifactInput[];
97
+ }): FirmwareUpdatePreparedArtifact[] => {
98
+ if (artifacts.length !== plan.artifacts.length) {
99
+ return preparedPlanError('Firmware prepared plan does not cover every artifact');
100
+ }
101
+ const inputs = new Map<string, FirmwareUpdatePreparedArtifactInput>();
102
+ for (const input of artifacts) {
103
+ if (
104
+ !input ||
105
+ typeof input !== 'object' ||
106
+ typeof input.artifactId !== 'string' ||
107
+ inputs.has(input.artifactId)
108
+ ) {
109
+ return preparedPlanError('Firmware prepared plan contains a duplicate artifact receipt');
110
+ }
111
+ inputs.set(input.artifactId, input);
112
+ }
113
+ return plan.artifacts.map(planArtifact => {
114
+ const input = inputs.get(planArtifact.artifactId);
115
+ if (!input) {
116
+ return preparedPlanError('Firmware prepared plan artifact receipt is missing');
117
+ }
118
+ const artifact = assertFirmwareArtifactReference(input.artifact);
119
+ if (
120
+ (planArtifact.expectedSize !== undefined && artifact.size !== planArtifact.expectedSize) ||
121
+ (planArtifact.expectedSha256 !== undefined &&
122
+ artifact.sha256.toLowerCase() !== planArtifact.expectedSha256.toLowerCase())
123
+ ) {
124
+ return preparedPlanError('Firmware prepared plan receipt does not match the update plan');
125
+ }
126
+ const materializedEntries = input.materializedEntries?.map(value => {
127
+ const entry = assertPreparedEntry(value);
128
+ return {
129
+ entryName: entry.entryName,
130
+ artifact: {
131
+ ...entry.artifact,
132
+ sha256: entry.artifact.sha256.toLowerCase(),
133
+ },
134
+ };
135
+ });
136
+ if (
137
+ (planArtifact.container === 'raw' && materializedEntries?.length) ||
138
+ (planArtifact.container === 'zip' && !materializedEntries?.length)
139
+ ) {
140
+ return preparedPlanError('Firmware prepared plan materialization is incomplete');
141
+ }
142
+ if (
143
+ materializedEntries &&
144
+ new Set(
145
+ materializedEntries.map(entry =>
146
+ getFirmwareUpdateResourceName(entry.entryName).toLowerCase()
147
+ )
148
+ ).size !== materializedEntries.length
149
+ ) {
150
+ return preparedPlanError('Firmware prepared plan contains duplicate entry names');
151
+ }
152
+ return {
153
+ artifactId: planArtifact.artifactId,
154
+ role: planArtifact.role,
155
+ target: planArtifact.target,
156
+ container: planArtifact.container,
157
+ ...(planArtifact.logicalName ? { logicalName: planArtifact.logicalName } : {}),
158
+ ...(planArtifact.targetVersion ? { targetVersion: planArtifact.targetVersion } : {}),
159
+ artifact: {
160
+ ...artifact,
161
+ sha256: artifact.sha256.toLowerCase(),
162
+ },
163
+ ...(materializedEntries?.length ? { materializedEntries } : {}),
164
+ };
165
+ });
166
+ };
167
+
168
+ export const prepareFirmwareUpdatePlan = ({
169
+ plan: value,
170
+ leaseRef,
171
+ artifacts,
172
+ }: {
173
+ plan: FirmwareUpdatePlan;
174
+ leaseRef: string;
175
+ artifacts: FirmwareUpdatePreparedArtifactInput[];
176
+ }): FirmwareUpdatePreparedPlan => {
177
+ const plan = assertFirmwareUpdatePlan(value);
178
+ const planWithoutDigest: Omit<FirmwareUpdatePreparedPlan, 'preparedPlanDigest'> = {
179
+ schemaVersion: 2,
180
+ planDigest: plan.planDigest,
181
+ networkPolicy: 'forbid',
182
+ executor: plan.executor,
183
+ deviceIdentity: plan.deviceIdentity,
184
+ deviceModel: plan.deviceModel,
185
+ firmwareType: plan.firmwareType,
186
+ platform: plan.platform,
187
+ leaseRef: assertOpaqueLeaseRef(leaseRef),
188
+ artifacts: assertPreparedArtifacts({ plan, artifacts }),
189
+ targetsToUpdate: plan.targetsToUpdate,
190
+ };
191
+ return {
192
+ ...planWithoutDigest,
193
+ preparedPlanDigest: digestPreparedPlan(planWithoutDigest),
194
+ };
195
+ };
196
+
197
+ export const validateFirmwareUpdatePreparedPlan = (value: unknown): FirmwareUpdatePreparedPlan => {
198
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
199
+ return preparedPlanError('Firmware prepared plan must be an object');
200
+ }
201
+ const preparedPlan = value as FirmwareUpdatePreparedPlan;
202
+ const exactKeys = [
203
+ 'schemaVersion',
204
+ 'preparedPlanDigest',
205
+ 'planDigest',
206
+ 'networkPolicy',
207
+ 'executor',
208
+ 'deviceIdentity',
209
+ 'deviceModel',
210
+ 'firmwareType',
211
+ 'platform',
212
+ 'leaseRef',
213
+ 'artifacts',
214
+ 'targetsToUpdate',
215
+ ];
216
+ if (
217
+ Object.keys(value).length !== exactKeys.length ||
218
+ exactKeys.some(key => !Object.prototype.hasOwnProperty.call(value, key)) ||
219
+ preparedPlan.schemaVersion !== 2 ||
220
+ preparedPlan.networkPolicy !== 'forbid' ||
221
+ !/^[a-f0-9]{64}$/u.test(preparedPlan.planDigest) ||
222
+ !/^[a-f0-9]{64}$/u.test(preparedPlan.preparedPlanDigest) ||
223
+ !Array.isArray(preparedPlan.artifacts) ||
224
+ !Array.isArray(preparedPlan.targetsToUpdate) ||
225
+ (preparedPlan.executor !== 'v2' &&
226
+ preparedPlan.executor !== 'v3' &&
227
+ preparedPlan.executor !== 'v4') ||
228
+ (preparedPlan.platform !== 'native' &&
229
+ preparedPlan.platform !== 'desktop' &&
230
+ preparedPlan.platform !== 'ext' &&
231
+ preparedPlan.platform !== 'web' &&
232
+ preparedPlan.platform !== 'web-embed') ||
233
+ (preparedPlan.firmwareType !== 'universal' && preparedPlan.firmwareType !== 'bitcoinonly') ||
234
+ typeof preparedPlan.deviceIdentity !== 'string' ||
235
+ preparedPlan.deviceIdentity.length === 0 ||
236
+ typeof preparedPlan.deviceModel !== 'string' ||
237
+ preparedPlan.deviceModel.length === 0
238
+ ) {
239
+ return preparedPlanError('Firmware prepared plan header is invalid');
240
+ }
241
+ assertOpaqueLeaseRef(preparedPlan.leaseRef);
242
+ const artifactIds = new Set<string>();
243
+ const artifactTargets = new Set<string>();
244
+ for (const artifact of preparedPlan.artifacts) {
245
+ if (!artifact || typeof artifact !== 'object') {
246
+ return preparedPlanError('Firmware prepared plan artifact is invalid');
247
+ }
248
+ const artifactKeys = [
249
+ 'artifactId',
250
+ 'role',
251
+ 'target',
252
+ 'container',
253
+ 'artifact',
254
+ ...(artifact.logicalName !== undefined ? ['logicalName'] : []),
255
+ ...(artifact.targetVersion !== undefined ? ['targetVersion'] : []),
256
+ ...(artifact.materializedEntries !== undefined ? ['materializedEntries'] : []),
257
+ ];
258
+ if (
259
+ typeof artifact.artifactId !== 'string' ||
260
+ artifact.artifactId.length === 0 ||
261
+ artifactIds.has(artifact.artifactId) ||
262
+ Object.keys(artifact).length !== artifactKeys.length ||
263
+ artifactKeys.some(key => !Object.prototype.hasOwnProperty.call(artifact, key)) ||
264
+ !FIRMWARE_UPDATE_PLAN_ROLES.has(artifact.role) ||
265
+ !FIRMWARE_UPDATE_PLAN_TARGETS.has(artifact.target) ||
266
+ (artifact.container !== 'raw' && artifact.container !== 'zip') ||
267
+ (artifact.logicalName !== undefined &&
268
+ (typeof artifact.logicalName !== 'string' || artifact.logicalName.length === 0)) ||
269
+ (artifact.targetVersion !== undefined &&
270
+ (typeof artifact.targetVersion !== 'string' || artifact.targetVersion.length === 0)) ||
271
+ (artifact.materializedEntries !== undefined &&
272
+ !Array.isArray(artifact.materializedEntries)) ||
273
+ (artifact.container === 'raw' && Boolean(artifact.materializedEntries?.length)) ||
274
+ (artifact.container === 'zip' && !artifact.materializedEntries?.length)
275
+ ) {
276
+ return preparedPlanError('Firmware prepared plan artifact is invalid');
277
+ }
278
+ artifactIds.add(artifact.artifactId);
279
+ artifactTargets.add(artifact.target);
280
+ assertFirmwareArtifactReference(artifact.artifact);
281
+ artifact.materializedEntries?.forEach(assertPreparedEntry);
282
+ }
283
+ if (
284
+ preparedPlan.targetsToUpdate.some(target => !FIRMWARE_UPDATE_PLAN_TARGETS.has(target)) ||
285
+ new Set(preparedPlan.targetsToUpdate).size !== preparedPlan.targetsToUpdate.length ||
286
+ preparedPlan.targetsToUpdate.length !== artifactTargets.size ||
287
+ preparedPlan.targetsToUpdate.some(target => !artifactTargets.has(target))
288
+ ) {
289
+ return preparedPlanError('Firmware prepared plan targets are invalid');
290
+ }
291
+ const { preparedPlanDigest, ...withoutDigest } = preparedPlan;
292
+ if (digestPreparedPlan(withoutDigest) !== preparedPlanDigest) {
293
+ return preparedPlanError('Firmware prepared plan digest is invalid');
294
+ }
295
+ return preparedPlan;
296
+ };
297
+
298
+ export const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({
299
+ preparedPlan: value,
300
+ deviceIdentity: reportedIdentity,
301
+ bootloaderMode,
302
+ deviceModel,
303
+ }: {
304
+ preparedPlan: unknown;
305
+ deviceIdentity: string | undefined;
306
+ /** Live device mode; only bootloader mode may accept a degraded-identity plan. */
307
+ bootloaderMode?: boolean;
308
+ /** Live device model; a degraded plan must target the same model. */
309
+ deviceModel?: string;
310
+ }): FirmwareUpdatePreparedPlan => {
311
+ const preparedPlan = validateFirmwareUpdatePreparedPlan(value);
312
+ // 'unavailable' is the reserved degraded sentinel — a device-reported serial equal
313
+ // to it must never satisfy the exact-match branch below.
314
+ const deviceIdentity = reportedIdentity === 'unavailable' ? undefined : reportedIdentity;
315
+ // Bootloader recovery: classic-family devices report no serial in bootloader mode,
316
+ // so a degraded plan is only acceptable when the live device is equally
317
+ // identity-less AND targets the same device model (identity equality implies the
318
+ // model match in the strict branch; the degraded branch must substitute it).
319
+ // A device that DOES report an identity must match the plan exactly.
320
+ if (
321
+ preparedPlan.deviceIdentity === 'unavailable' &&
322
+ !deviceIdentity &&
323
+ bootloaderMode === true &&
324
+ deviceModel !== undefined &&
325
+ preparedPlan.deviceModel === deviceModel
326
+ ) {
327
+ return preparedPlan;
328
+ }
329
+ if (!deviceIdentity || preparedPlan.deviceIdentity !== deviceIdentity) {
330
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
331
+ }
332
+ return preparedPlan;
333
+ };
334
+
335
+ type FirmwareUpdatePreparedBinding = {
336
+ target: FirmwareUpdatePlan['targetsToUpdate'][number];
337
+ artifact: FirmwareArtifactReference;
338
+ logicalName?: string;
339
+ entryName?: string;
340
+ };
341
+
342
+ const sameArtifactReference = (
343
+ first: FirmwareArtifactReference,
344
+ second: FirmwareArtifactReference
345
+ ) =>
346
+ first.artifactRef === second.artifactRef &&
347
+ first.size === second.size &&
348
+ first.sha256.toLowerCase() === second.sha256.toLowerCase();
349
+
350
+ export const assertFirmwareUpdatePreparedPlanBinding = ({
351
+ preparedPlan: value,
352
+ executor,
353
+ platform,
354
+ deviceIdentity,
355
+ scopeTargets,
356
+ bindings,
357
+ }: {
358
+ preparedPlan: unknown;
359
+ executor: FirmwareUpdatePreparedPlan['executor'];
360
+ platform?: FirmwareUpdatePreparedPlan['platform'];
361
+ deviceIdentity?: string;
362
+ scopeTargets: FirmwareUpdatePreparedPlan['targetsToUpdate'];
363
+ bindings: FirmwareUpdatePreparedBinding[];
364
+ }): FirmwareUpdatePreparedPlan => {
365
+ const preparedPlan = validateFirmwareUpdatePreparedPlan(value);
366
+ if (
367
+ preparedPlan.executor !== executor ||
368
+ (platform !== undefined && preparedPlan.platform !== platform) ||
369
+ (deviceIdentity !== undefined && preparedPlan.deviceIdentity !== deviceIdentity)
370
+ ) {
371
+ return preparedPlanError('Firmware prepared plan executor binding is invalid');
372
+ }
373
+ const scope = new Set(scopeTargets);
374
+ const expectedBindings: FirmwareUpdatePreparedBinding[] = [];
375
+ for (const artifact of preparedPlan.artifacts) {
376
+ if (scope.has(artifact.target)) {
377
+ if (artifact.container === 'zip') {
378
+ for (const entry of artifact.materializedEntries ?? []) {
379
+ expectedBindings.push({
380
+ target: artifact.target,
381
+ ...(artifact.target === 'resource' && artifact.logicalName
382
+ ? { logicalName: artifact.logicalName }
383
+ : {}),
384
+ entryName: entry.entryName,
385
+ artifact: entry.artifact,
386
+ });
387
+ }
388
+ } else {
389
+ expectedBindings.push({
390
+ target: artifact.target,
391
+ ...(artifact.target === 'resource' && artifact.logicalName
392
+ ? { logicalName: artifact.logicalName }
393
+ : {}),
394
+ artifact: artifact.artifact,
395
+ });
396
+ }
397
+ }
398
+ }
399
+ if (
400
+ expectedBindings.length !== bindings.length ||
401
+ expectedBindings.some(expected => {
402
+ const matching = bindings.filter(
403
+ binding =>
404
+ binding.target === expected.target &&
405
+ binding.logicalName === expected.logicalName &&
406
+ binding.entryName === expected.entryName &&
407
+ sameArtifactReference(binding.artifact, expected.artifact)
408
+ );
409
+ return matching.length !== 1;
410
+ })
411
+ ) {
412
+ return preparedPlanError('Firmware prepared plan artifact binding is invalid');
413
+ }
414
+ return preparedPlan;
415
+ };
@@ -0,0 +1,44 @@
1
+ import type { IFirmwareUpdateProgressType } from '../../events/ui-request';
2
+
3
+ const DEFAULT_FIRMWARE_PROGRESS_INTERVAL_MS = 250;
4
+
5
+ type FirmwareProgressThrottleState = {
6
+ lastEmittedAt: number;
7
+ lastProgress?: number;
8
+ };
9
+
10
+ export const createFirmwareProgressThrottle = (
11
+ intervalMs = DEFAULT_FIRMWARE_PROGRESS_INTERVAL_MS
12
+ ) => {
13
+ const stateByType = new Map<IFirmwareUpdateProgressType, FirmwareProgressThrottleState>();
14
+
15
+ return (progress: number, progressType: IFirmwareUpdateProgressType) => {
16
+ if (progressType !== 'transferData') {
17
+ return true;
18
+ }
19
+
20
+ const normalizedProgress = Math.max(0, Math.min(100, Math.floor(progress)));
21
+ if (normalizedProgress === 0 || normalizedProgress === 100) {
22
+ stateByType.set(progressType, {
23
+ lastEmittedAt: Date.now(),
24
+ lastProgress: normalizedProgress,
25
+ });
26
+ return true;
27
+ }
28
+
29
+ const now = Date.now();
30
+ const state = stateByType.get(progressType);
31
+ if (
32
+ state &&
33
+ (state.lastProgress === normalizedProgress || now - state.lastEmittedAt < intervalMs)
34
+ ) {
35
+ return false;
36
+ }
37
+
38
+ stateByType.set(progressType, {
39
+ lastEmittedAt: now,
40
+ lastProgress: normalizedProgress,
41
+ });
42
+ return true;
43
+ };
44
+ };
@@ -8,6 +8,7 @@ import { shouldUpdateBootloaderForClassicAndMini } from './bootloaderHelper';
8
8
 
9
9
  import type { Features } from '../../types';
10
10
  import type { EFirmwareType } from '@onekeyfe/hd-shared';
11
+ import type { FirmwareByteSource } from './FirmwareArtifactSource';
11
12
 
12
13
  export function checkNeedUpdateBootForTouch(features: Features, firmwareType: EFirmwareType) {
13
14
  const deviceType = getDeviceType(features);
@@ -62,8 +63,7 @@ export function checkNeedUpdateBootForClassicAndMini({
62
63
  }
63
64
 
64
65
  const INIT_DATA_CHUNK_SIZE = 16 * 1024;
65
- export function checkBootloaderLength(data: ArrayBuffer) {
66
- const chunk = new Uint8Array(data.slice(0, Math.min(INIT_DATA_CHUNK_SIZE, data.byteLength)));
66
+ const readBootloaderLength = (chunk: Uint8Array) => {
67
67
  const buffer = ByteBuffer.wrap(chunk, undefined, undefined, true);
68
68
  buffer.LE();
69
69
  // byte 'O', 'K', 'T', 'B'
@@ -77,6 +77,21 @@ export function checkBootloaderLength(data: ArrayBuffer) {
77
77
  buffer.readUint32();
78
78
  // codelen
79
79
  const codelen = buffer.readUint32();
80
- const bootloaderLength = hdrlen + codelen;
81
- return bootloaderLength === data.byteLength;
80
+ return hdrlen + codelen;
81
+ };
82
+
83
+ export function checkBootloaderLength(data: ArrayBuffer) {
84
+ if (data.byteLength < 16) {
85
+ return false;
86
+ }
87
+ const chunk = new Uint8Array(data.slice(0, Math.min(INIT_DATA_CHUNK_SIZE, data.byteLength)));
88
+ return readBootloaderLength(chunk) === data.byteLength;
82
89
  }
90
+
91
+ export const checkBootloaderSourceLength = async (source: FirmwareByteSource) => {
92
+ if (source.size < 16) {
93
+ return false;
94
+ }
95
+ const chunk = new Uint8Array(await source.readAt(0, Math.min(INIT_DATA_CHUNK_SIZE, source.size)));
96
+ return readBootloaderLength(chunk) === source.size;
97
+ };