@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
@@ -1,31 +0,0 @@
1
- export declare const PRO2_NFT_IMAGE_WIDTH = 540;
2
- export declare const PRO2_NFT_IMAGE_HEIGHT = 540;
3
- export declare const PRO2_NFT_THUMBNAIL_WIDTH = 263;
4
- export declare const PRO2_NFT_THUMBNAIL_HEIGHT = 263;
5
- export declare const PRO2_NFT_DIRECTORY = "vol1:/nft";
6
- export declare const PRO2_NFT_DEFAULT_CHUNK_SIZE = 2048;
7
- export declare const PRO2_NFT_DEFAULT_PACE_MS = 0;
8
- export declare const PRO2_NFT_DEFAULT_TIMEOUT_MS = 15000;
9
- export declare const PRO2_NFT_MIN_CHUNK_SIZE = 64;
10
- export declare const PRO2_NFT_MAX_CHUNK_SIZE = 2048;
11
- export declare const PRO2_NFT_MAX_ITEMS = 10;
12
- export type Pro2NftImage = {
13
- width: number;
14
- height: number;
15
- rgba: Uint8Array | ArrayBuffer;
16
- };
17
- export type Pro2NftBundle = {
18
- basename: string;
19
- image: Uint8Array;
20
- thumbnail: Uint8Array;
21
- metadata: Uint8Array;
22
- };
23
- export declare function getCompletePro2NftBasenames(childFiles?: string): Set<string>;
24
- export declare function buildPro2NftBundle(options: {
25
- image: Pro2NftImage;
26
- thumbnail: Pro2NftImage;
27
- title: string;
28
- subtitle: string;
29
- timestampMs: number;
30
- }): Pro2NftBundle;
31
- //# sourceMappingURL=pro2Nft.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pro2Nft.d.ts","sourceRoot":"","sources":["../../src/utils/pro2Nft.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAC9C,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAClD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,GAAG,WAAW,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,UAAU,CAAC;CACtB,CAAC;AAiCF,wBAAgB,2BAA2B,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAiB5E;AAsBD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,YAAY,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,aAAa,CAkChB"}
@@ -1,3 +0,0 @@
1
- import type { CoreMessage } from '../events';
2
- export declare const createUiProgressMessageFilter: (intervalMs?: number, now?: () => number) => (message: CoreMessage) => boolean;
3
- //# sourceMappingURL=uiProgressThrottle.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"uiProgressThrottle.d.ts","sourceRoot":"","sources":["../../src/utils/uiProgressThrottle.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAiB7C,eAAO,MAAM,6BAA6B,yDAMvB,WAAW,YAqC7B,CAAC"}
@@ -1,18 +0,0 @@
1
- import { BaseMethod } from './BaseMethod';
2
-
3
- export default class DetectDeviceConnectProtocol extends BaseMethod {
4
- init() {
5
- this.payload.forceProtocolDetection = true;
6
- this.useDevicePassphraseState = false;
7
- this.skipForceUpdateCheck = true;
8
- this.unlockPolicy = 'none';
9
- }
10
-
11
- getSupportedProtocols() {
12
- return ['V1', 'V2'] as const;
13
- }
14
-
15
- run() {
16
- return Promise.resolve(this.device.getProtocol());
17
- }
18
- }
@@ -1,189 +0,0 @@
1
- import { ERRORS, HardwareErrorCode, createDeviceNotSupportMethodError } from '@onekeyfe/hd-shared';
2
-
3
- import { UI_REQUEST, createUiMessage } from '../../events/ui-request';
4
- import { supportsProtocolV2Message } from '../../protocols/protocol-v2/features';
5
- import {
6
- PRO2_NFT_DEFAULT_CHUNK_SIZE,
7
- PRO2_NFT_DEFAULT_PACE_MS,
8
- PRO2_NFT_DEFAULT_TIMEOUT_MS,
9
- PRO2_NFT_DIRECTORY,
10
- PRO2_NFT_MAX_CHUNK_SIZE,
11
- PRO2_NFT_MAX_ITEMS,
12
- PRO2_NFT_MIN_CHUNK_SIZE,
13
- type Pro2NftBundle,
14
- type Pro2NftImage,
15
- buildPro2NftBundle,
16
- getCompletePro2NftBasenames,
17
- } from '../../utils/pro2Nft';
18
- import { BaseMethod } from '../BaseMethod';
19
- import { invalidParameter } from '../helpers/filesystemValidation';
20
- import { isProtocolV2ResponseTimeout, writeProtocolV2File } from '../helpers/protocolV2FileWrite';
21
-
22
- export type DeviceUploadNftParams = {
23
- image: Pro2NftImage;
24
- thumbnail: Pro2NftImage;
25
- title: string;
26
- subtitle: string;
27
- timestampMs?: number;
28
- chunkSize?: number;
29
- paceMs?: number;
30
- timeoutMs?: number;
31
- };
32
-
33
- export type DeviceUploadNftResponse = {
34
- basename: string;
35
- imagePath: string;
36
- thumbnailPath: string;
37
- metadataPath: string;
38
- totalSize: number;
39
- nftUpdated: true;
40
- message?: string;
41
- };
42
-
43
- const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
44
- const FILESYSTEM_DIR_LIST_MESSAGE_TYPE = 60808;
45
- const NFT_UPDATE_MESSAGE_TYPE = 61500;
46
-
47
- export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
48
- private bundle?: Pro2NftBundle;
49
-
50
- getSupportedProtocols() {
51
- return ['V2'] as const;
52
- }
53
-
54
- init() {
55
- const {
56
- image,
57
- thumbnail,
58
- title,
59
- subtitle,
60
- timestampMs = Date.now(),
61
- chunkSize = PRO2_NFT_DEFAULT_CHUNK_SIZE,
62
- paceMs = PRO2_NFT_DEFAULT_PACE_MS,
63
- timeoutMs = PRO2_NFT_DEFAULT_TIMEOUT_MS,
64
- } = this.payload;
65
- if (
66
- !Number.isInteger(chunkSize) ||
67
- chunkSize < PRO2_NFT_MIN_CHUNK_SIZE ||
68
- chunkSize > PRO2_NFT_MAX_CHUNK_SIZE
69
- ) {
70
- throw invalidParameter(
71
- `Parameter [chunkSize] must be an integer between ${PRO2_NFT_MIN_CHUNK_SIZE} and ${PRO2_NFT_MAX_CHUNK_SIZE}.`
72
- );
73
- }
74
- if (!Number.isInteger(paceMs) || paceMs < 0) {
75
- throw invalidParameter('Parameter [paceMs] must be a non-negative integer.');
76
- }
77
- if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
78
- throw invalidParameter('Parameter [timeoutMs] must be a positive integer.');
79
- }
80
-
81
- this.bundle = buildPro2NftBundle({ image, thumbnail, title, subtitle, timestampMs });
82
- this.params = { image, thumbnail, title, subtitle, timestampMs, chunkSize, paceMs, timeoutMs };
83
- this.unlockPolicy = 'none';
84
- this.skipForceUpdateCheck = true;
85
- this.useDevicePassphraseState = false;
86
- }
87
-
88
- private async assertCapabilities() {
89
- const protocolInfo = await this.device.ensureProtocolV2RuntimeContext();
90
- const hasFileWrite = supportsProtocolV2Message(
91
- protocolInfo,
92
- FILESYSTEM_FILE_WRITE_MESSAGE_TYPE
93
- );
94
- const hasDirList = supportsProtocolV2Message(protocolInfo, FILESYSTEM_DIR_LIST_MESSAGE_TYPE);
95
- const hasNftUpdate = supportsProtocolV2Message(protocolInfo, NFT_UPDATE_MESSAGE_TYPE);
96
- if (!hasFileWrite || !hasDirList || !hasNftUpdate) {
97
- throw createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
98
- }
99
- }
100
-
101
- private async assertStorageCapacity(basename: string) {
102
- const { message } = await this.device.commands.typedCall(
103
- 'FilesystemDirList',
104
- 'FilesystemDir',
105
- { path: PRO2_NFT_DIRECTORY, depth: 1 },
106
- { timeoutMs: this.params.timeoutMs }
107
- );
108
- const existingBasenames = getCompletePro2NftBasenames(message.child_files);
109
- if (existingBasenames.size >= PRO2_NFT_MAX_ITEMS && !existingBasenames.has(basename)) {
110
- throw ERRORS.TypedError(HardwareErrorCode.NftStorageLimitReached, undefined, {
111
- count: existingBasenames.size,
112
- limit: PRO2_NFT_MAX_ITEMS,
113
- });
114
- }
115
- }
116
-
117
- private async updateNft(basename: string) {
118
- const params = { file_name_no_ext: basename };
119
- const options = { timeoutMs: this.params.timeoutMs };
120
- try {
121
- return await this.device.commands.typedCall('NftUpdate', 'Success', params, options);
122
- } catch (error) {
123
- if (!isProtocolV2ResponseTimeout(error)) {
124
- throw error;
125
- }
126
- this.throwIfAborted();
127
- return this.device.commands.typedCall('NftUpdate', 'Success', params, options);
128
- }
129
- }
130
-
131
- async run(): Promise<DeviceUploadNftResponse> {
132
- const { bundle } = this;
133
- if (!bundle) throw invalidParameter('NFT data has not been initialized.');
134
-
135
- await this.assertCapabilities();
136
- this.throwIfAborted();
137
- await this.assertStorageCapacity(bundle.basename);
138
- this.throwIfAborted();
139
-
140
- const files = [
141
- { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.bin`, data: bundle.image },
142
- { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}_m.bin`, data: bundle.thumbnail },
143
- { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.json`, data: bundle.metadata },
144
- ];
145
- const totalSize = files.reduce((sum, file) => sum + file.data.byteLength, 0);
146
- let transferredBeforeFile = 0;
147
-
148
- for (const file of files) {
149
- const transferredAtFileStart = transferredBeforeFile;
150
- await writeProtocolV2File({
151
- commands: this.device.commands,
152
- path: file.path,
153
- data: file.data,
154
- totalSize: file.data.byteLength,
155
- chunkSize: this.params.chunkSize,
156
- timeoutMs: this.params.timeoutMs,
157
- paceMs: this.params.paceMs,
158
- overwrite: true,
159
- append: false,
160
- throwIfAborted: () => this.throwIfAborted(),
161
- onProgress: progress => {
162
- if (typeof this.postMessage !== 'function') return;
163
- const transferredBytes = transferredAtFileStart + progress.transferredBytes;
164
- this.postMessage(
165
- createUiMessage(UI_REQUEST.DEVICE_PROGRESS, {
166
- ...progress,
167
- progress: Math.floor((transferredBytes / totalSize) * 100),
168
- transferredBytes,
169
- totalBytes: totalSize,
170
- })
171
- );
172
- },
173
- });
174
- transferredBeforeFile += file.data.byteLength;
175
- }
176
-
177
- this.throwIfAborted();
178
- const response = await this.updateNft(bundle.basename);
179
- return {
180
- basename: bundle.basename,
181
- imagePath: files[0].path,
182
- thumbnailPath: files[1].path,
183
- metadataPath: files[2].path,
184
- totalSize,
185
- nftUpdated: true,
186
- message: response.message?.message,
187
- };
188
- }
189
- }
@@ -1,41 +0,0 @@
1
- import { UI_RESPONSE } from '../events/ui-response';
2
-
3
- import type { UiPromise, UiPromiseResponse } from '../events/ui-promise';
4
- import type { UiResponseEvent } from '../events/ui-response';
5
-
6
- const isSensitiveUiResponse = (
7
- response: UiResponseEvent
8
- ): response is Extract<
9
- UiResponseEvent,
10
- { type: typeof UI_RESPONSE.RECEIVE_PIN | typeof UI_RESPONSE.RECEIVE_PASSPHRASE }
11
- > => response.type === UI_RESPONSE.RECEIVE_PIN || response.type === UI_RESPONSE.RECEIVE_PASSPHRASE;
12
-
13
- export const findUiPromiseForResponse = (
14
- uiPromises: UiPromise<UiPromiseResponse['type']>[],
15
- response: UiResponseEvent
16
- ) => {
17
- const candidates = uiPromises.filter(promise => promise.id === response.type);
18
- if (!isSensitiveUiResponse(response)) {
19
- return candidates[0];
20
- }
21
-
22
- const hasInteractionId = typeof response.interactionId === 'string';
23
- const hasDeviceId = typeof response.deviceId === 'string';
24
- if (hasInteractionId !== hasDeviceId) {
25
- return undefined;
26
- }
27
-
28
- if (hasInteractionId && hasDeviceId) {
29
- return candidates.find(promise => {
30
- const correlation = promise.responseCorrelation;
31
- return (
32
- correlation?.interactionId === response.interactionId &&
33
- correlation?.deviceId === response.deviceId
34
- );
35
- });
36
- }
37
-
38
- // Legacy clients do not return correlation metadata. Preserve their single-request
39
- // behavior, but never guess when multiple sensitive requests are pending.
40
- return candidates.length === 1 ? candidates[0] : undefined;
41
- };
@@ -1,359 +0,0 @@
1
- import { sha256 } from '@noble/hashes/sha256';
2
- import { PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE } from '@onekeyfe/hd-transport';
3
-
4
- import type {
5
- IProtocolV2BootResources,
6
- IProtocolV2Resource,
7
- IProtocolV2ResourceType,
8
- IProtocolV2Resources,
9
- } from '../../types';
10
- import type { DeviceCommands } from '../../device/DeviceCommands';
11
-
12
- export const PROTOCOL_V2_RESOURCE_TYPES = [
13
- 'images',
14
- 'animation',
15
- 'wallpaper',
16
- 'translations',
17
- 'roobert',
18
- 'noto',
19
- ] as const satisfies readonly IProtocolV2ResourceType[];
20
-
21
- export const PROTOCOL_V2_RESOURCE_DEVICE_PATHS: Readonly<Record<IProtocolV2ResourceType, string>> =
22
- {
23
- images: 'vol0:/bundles/images/images.okpkg',
24
- animation: 'vol0:/bundles/images/animation.okpkg',
25
- wallpaper: 'vol0:/bundles/images/wallpaper.okpkg',
26
- translations: 'vol0:/bundles/translations/translations.okpkg',
27
- roobert: 'vol0:/bundles/font/roobert.okpkg',
28
- noto: 'vol0:/bundles/font/noto.okpkg',
29
- };
30
-
31
- const RESOURCE_TYPE_SET = new Set<string>(PROTOCOL_V2_RESOURCE_TYPES);
32
- const SHA256_HEX_LENGTH = 64;
33
- const SHA3_512_HEX_LENGTH = 128;
34
- const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
35
- const PROTOCOL_V2_OKPP_TYPE_OFFSET = 0x08;
36
- const PROTOCOL_V2_OKPP_HEADER_LENGTH_OFFSET = 0x0c;
37
- const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
38
- const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
39
- const PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE =
40
- PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET + PROTOCOL_V2_OKPP_HASH_SIZE;
41
- const PROTOCOL_V2_MIN_FILE_READ_CHUNK_SIZE = 64;
42
- export const PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS = 5 * 1000;
43
-
44
- export type ProtocolV2ResourceInventoryItem = {
45
- type: IProtocolV2ResourceType;
46
- size: number;
47
- headerHash: string;
48
- };
49
-
50
- export type ProtocolV2ResourceUpdateMode = 'application' | 'bootloader-recovery';
51
-
52
- export type ProtocolV2ResourceUpdatePlan = {
53
- status: 'valid' | 'outdated' | 'unknown';
54
- resources: IProtocolV2Resource[];
55
- };
56
-
57
- function toFiniteNumber(value: unknown): number | undefined {
58
- if (typeof value === 'number' && Number.isFinite(value)) return value;
59
- if (typeof value === 'string') {
60
- const numeric = Number(value);
61
- return Number.isFinite(numeric) ? numeric : undefined;
62
- }
63
- if (value && typeof value === 'object') {
64
- const longLike = value as { toNumber?: () => number };
65
- if (typeof longLike.toNumber === 'function') {
66
- const numeric = longLike.toNumber();
67
- return Number.isFinite(numeric) ? numeric : undefined;
68
- }
69
- }
70
- return undefined;
71
- }
72
-
73
- function toUint8Array(value: unknown): Uint8Array {
74
- if (value instanceof Uint8Array) return value;
75
- if (value instanceof ArrayBuffer) return new Uint8Array(value);
76
- if (ArrayBuffer.isView(value)) {
77
- return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
78
- }
79
- if (typeof value === 'string') {
80
- const hex = value.replace(/^0x/i, '');
81
- if (!hex || hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) {
82
- return new Uint8Array(0);
83
- }
84
- const bytes = new Uint8Array(hex.length / 2);
85
- for (let index = 0; index < bytes.length; index += 1) {
86
- bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
87
- }
88
- return bytes;
89
- }
90
- return new Uint8Array(0);
91
- }
92
-
93
- function readAscii(bytes: Uint8Array, offset: number, length: number): string {
94
- return Array.from(bytes.slice(offset, offset + length), byte => String.fromCharCode(byte)).join(
95
- ''
96
- );
97
- }
98
-
99
- function parseProtocolV2ResourceHeaderHash(bytes: Uint8Array): string | undefined {
100
- if (bytes.byteLength < PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE) return undefined;
101
- if (readAscii(bytes, 0, 4) !== 'OKPP') return undefined;
102
- if (readAscii(bytes, PROTOCOL_V2_OKPP_TYPE_OFFSET, 4) !== 'RESC') return undefined;
103
-
104
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
105
- if (
106
- view.getUint32(PROTOCOL_V2_OKPP_HEADER_LENGTH_OFFSET, true) !== PROTOCOL_V2_OKPP_HEADER_SIZE
107
- ) {
108
- return undefined;
109
- }
110
- return bytesToHex(
111
- bytes.slice(
112
- PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET,
113
- PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET + PROTOCOL_V2_OKPP_HASH_SIZE
114
- )
115
- );
116
- }
117
-
118
- async function readProtocolV2ResourceIdentity({
119
- commands,
120
- resource,
121
- chunkSize,
122
- timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS,
123
- }: {
124
- commands: Pick<DeviceCommands, 'typedCall'>;
125
- resource: IProtocolV2Resource;
126
- chunkSize: number;
127
- timeoutMs?: number;
128
- }): Promise<ProtocolV2ResourceInventoryItem | undefined> {
129
- const path = PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type];
130
- const pathInfo = await commands.typedCall(
131
- 'FilesystemPathInfoQuery',
132
- 'FilesystemPathInfo',
133
- { path },
134
- { timeoutMs }
135
- );
136
- const size = toFiniteNumber(pathInfo.message?.size);
137
- if (
138
- !pathInfo.message?.exist ||
139
- pathInfo.message?.directory ||
140
- !Number.isSafeInteger(size) ||
141
- size !== resource.size ||
142
- size < PROTOCOL_V2_OKPP_HEADER_SIZE
143
- ) {
144
- return undefined;
145
- }
146
-
147
- const header = new Uint8Array(PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE);
148
- let offset = 0;
149
- while (offset < header.byteLength) {
150
- const readLength = Math.min(chunkSize, header.byteLength - offset);
151
- const response = await commands.typedCall(
152
- 'FilesystemFileRead',
153
- 'FilesystemFile',
154
- {
155
- file: { path, offset, total_size: 0 },
156
- chunk_len: readLength,
157
- },
158
- { timeoutMs }
159
- );
160
- const data = toUint8Array(response.message?.data);
161
- if (data.byteLength === 0) return undefined;
162
- const copied = Math.min(data.byteLength, header.byteLength - offset);
163
- header.set(data.subarray(0, copied), offset);
164
- offset += copied;
165
- }
166
-
167
- const headerHash = parseProtocolV2ResourceHeaderHash(header);
168
- return headerHash ? { type: resource.type, size, headerHash } : undefined;
169
- }
170
-
171
- /**
172
- * 使用已发布 Pro2 固件支持的文件系统消息构建资源清单。
173
- * 缺失、无法读取或格式错误的文件不会进入清单,因此会被选中重写。
174
- */
175
- export async function readProtocolV2ResourceInventory({
176
- commands,
177
- resources,
178
- chunkSize = PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE,
179
- timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS,
180
- }: {
181
- commands: Pick<DeviceCommands, 'typedCall'>;
182
- resources: readonly IProtocolV2Resource[];
183
- chunkSize?: number;
184
- timeoutMs?: number;
185
- }): Promise<ProtocolV2ResourceInventoryItem[]> {
186
- const normalizedChunkSize = Number.isFinite(chunkSize)
187
- ? Math.max(Math.floor(chunkSize), PROTOCOL_V2_MIN_FILE_READ_CHUNK_SIZE)
188
- : PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
189
- const inventory: ProtocolV2ResourceInventoryItem[] = [];
190
- for (const resource of resources) {
191
- try {
192
- const item = await readProtocolV2ResourceIdentity({
193
- commands,
194
- resource,
195
- chunkSize: normalizedChunkSize,
196
- timeoutMs,
197
- });
198
- if (item) inventory.push(item);
199
- } catch {
200
- // 单个资源无法读取时按缺失处理,不阻断其余资源的增量检查。
201
- }
202
- }
203
- return inventory;
204
- }
205
-
206
- function normalizeHex(value: unknown, expectedLength: number, field: string): string {
207
- if (typeof value !== 'string') {
208
- throw new Error(`Invalid Pro2 resource ${field}: expected a hexadecimal string`);
209
- }
210
- const normalized = value.replace(/^0x/i, '').toLowerCase();
211
- if (normalized.length !== expectedLength || !/^[0-9a-f]+$/.test(normalized)) {
212
- throw new Error(
213
- `Invalid Pro2 resource ${field}: expected ${expectedLength} hexadecimal characters`
214
- );
215
- }
216
- return normalized;
217
- }
218
-
219
- function validateResource(value: unknown, index: number): IProtocolV2Resource {
220
- if (!value || typeof value !== 'object') {
221
- throw new Error(`Invalid Pro2 resource at stable[${index}]`);
222
- }
223
- const resource = value as Partial<IProtocolV2Resource>;
224
- if (typeof resource.type !== 'string' || !RESOURCE_TYPE_SET.has(resource.type)) {
225
- throw new Error(`Invalid Pro2 resource type at stable[${index}]`);
226
- }
227
- if (typeof resource.url !== 'string' || !resource.url.startsWith('https://')) {
228
- throw new Error(`Invalid Pro2 resource url at stable[${index}]`);
229
- }
230
- if (!Number.isSafeInteger(resource.size) || Number(resource.size) <= 0) {
231
- throw new Error(`Invalid Pro2 resource size at stable[${index}]`);
232
- }
233
- return {
234
- type: resource.type,
235
- url: resource.url,
236
- size: Number(resource.size),
237
- fileHash: normalizeHex(resource.fileHash, SHA256_HEX_LENGTH, 'fileHash'),
238
- headerHash: normalizeHex(resource.headerHash, SHA3_512_HEX_LENGTH, 'headerHash'),
239
- };
240
- }
241
-
242
- function validateBootResources(value: unknown): IProtocolV2BootResources {
243
- if (!value || typeof value !== 'object') {
244
- throw new Error('Invalid Pro2 boot resources config');
245
- }
246
- const resource = value as Partial<IProtocolV2BootResources>;
247
- if (resource.required !== false) {
248
- throw new Error('Invalid Pro2 boot resources required flag: expected false');
249
- }
250
- if (resource.target !== 'CRATE') {
251
- throw new Error('Invalid Pro2 boot resources target: expected CRATE');
252
- }
253
- if (typeof resource.url !== 'string' || !resource.url.startsWith('https://')) {
254
- throw new Error('Invalid Pro2 boot resources url');
255
- }
256
- if (!Number.isSafeInteger(resource.size) || Number(resource.size) <= 0) {
257
- throw new Error('Invalid Pro2 boot resources size');
258
- }
259
- return {
260
- required: false,
261
- target: 'CRATE',
262
- url: resource.url,
263
- size: Number(resource.size),
264
- fileHash: normalizeHex(resource.fileHash, SHA256_HEX_LENGTH, 'boot fileHash'),
265
- payloadHash: normalizeHex(resource.payloadHash, SHA3_512_HEX_LENGTH, 'boot payloadHash'),
266
- headerHash: normalizeHex(resource.headerHash, SHA3_512_HEX_LENGTH, 'boot headerHash'),
267
- };
268
- }
269
-
270
- /** Validate a complete Pro2 stable resource set from remote configuration. */
271
- export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources | undefined {
272
- if (value === undefined) return undefined;
273
- if (
274
- !value ||
275
- typeof value !== 'object' ||
276
- !Array.isArray((value as { stable?: unknown }).stable)
277
- ) {
278
- throw new Error('Invalid Pro2 resources config: stable must be an array');
279
- }
280
-
281
- const config = value as { stable: unknown[]; boot?: unknown };
282
- const stable = config.stable.map(validateResource);
283
- const types = new Set(stable.map(resource => resource.type));
284
- if (stable.length !== PROTOCOL_V2_RESOURCE_TYPES.length || types.size !== stable.length) {
285
- throw new Error('Invalid Pro2 resources config: stable must contain six unique resource types');
286
- }
287
- for (const type of PROTOCOL_V2_RESOURCE_TYPES) {
288
- if (!types.has(type)) {
289
- throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
290
- }
291
- }
292
- const boot = config.boot === undefined ? undefined : validateBootResources(config.boot);
293
- return {
294
- stable: PROTOCOL_V2_RESOURCE_TYPES.map(type => {
295
- const resource = stable.find(item => item.type === type);
296
- if (!resource) {
297
- throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
298
- }
299
- return resource;
300
- }),
301
- ...(boot ? { boot } : undefined),
302
- };
303
- }
304
-
305
- /** 比较文件系统资源清单;恢复模式无法取得清单时回退到全量更新。 */
306
- export function buildProtocolV2ResourceUpdatePlan({
307
- resources,
308
- inventory,
309
- mode,
310
- forced = false,
311
- }: {
312
- resources: readonly IProtocolV2Resource[];
313
- inventory?: readonly ProtocolV2ResourceInventoryItem[];
314
- mode: ProtocolV2ResourceUpdateMode;
315
- forced?: boolean;
316
- }): ProtocolV2ResourceUpdatePlan {
317
- if (forced) {
318
- return {
319
- status: resources.length > 0 ? 'outdated' : 'valid',
320
- resources: [...resources],
321
- };
322
- }
323
- if (!inventory) {
324
- return mode === 'bootloader-recovery'
325
- ? {
326
- status: resources.length > 0 ? 'outdated' : 'valid',
327
- resources: [...resources],
328
- }
329
- : { status: 'unknown', resources: [] };
330
- }
331
-
332
- const inventoryByType = new Map(inventory.map(item => [item.type, item]));
333
- const changedResources = resources.filter(resource => {
334
- const current = inventoryByType.get(resource.type);
335
- return (
336
- !current ||
337
- current.size !== resource.size ||
338
- current.headerHash.toLowerCase() !== resource.headerHash.toLowerCase()
339
- );
340
- });
341
-
342
- return {
343
- status: changedResources.length === 0 ? 'valid' : 'outdated',
344
- resources: changedResources,
345
- };
346
- }
347
-
348
- function bytesToHex(bytes: Uint8Array): string {
349
- return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
350
- }
351
-
352
- /** Verify the complete downloaded file before any device mutation. */
353
- export function isProtocolV2ResourceFileValid(
354
- binary: ArrayBuffer,
355
- resource: Pick<IProtocolV2Resource, 'size' | 'fileHash'>
356
- ): boolean {
357
- if (binary.byteLength !== resource.size) return false;
358
- return bytesToHex(sha256(new Uint8Array(binary))) === resource.fileHash.toLowerCase();
359
- }