@onekeyfe/hd-core 1.2.0-alpha.162 → 1.2.0-alpha.164

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 (40) hide show
  1. package/__tests__/device-state-events.test.ts +2 -2
  2. package/__tests__/device-state-mapper.test.ts +2 -11
  3. package/__tests__/device-utils.test.ts +0 -6
  4. package/__tests__/firmware-memory-host.test.ts +126 -0
  5. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +3 -13
  6. package/__tests__/protocol-v2-resources.test.ts +2 -35
  7. package/__tests__/protocol-v2.test.ts +42 -89
  8. package/__tests__/search-devices.test.ts +3 -4
  9. package/dist/api/FirmwareUpdateV4.d.ts +1 -3
  10. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  11. package/dist/api/firmware/FirmwareMemoryHost.d.ts +22 -0
  12. package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
  13. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +15 -0
  14. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  15. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
  16. package/dist/core/index.d.ts.map +1 -1
  17. package/dist/device/Device.d.ts.map +1 -1
  18. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  19. package/dist/index.d.ts +22 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +298 -102
  22. package/dist/protocols/protocol-v2/resources.d.ts +0 -1
  23. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  24. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  25. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  26. package/package.json +4 -4
  27. package/src/api/FirmwareUpdateV4.ts +179 -75
  28. package/src/api/SearchDevices.ts +2 -2
  29. package/src/api/firmware/FirmwareMemoryHost.ts +143 -0
  30. package/src/api/firmware/FirmwareUpdatePlan.ts +41 -0
  31. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +6 -10
  32. package/src/core/index.ts +97 -6
  33. package/src/device/Device.ts +1 -3
  34. package/src/device/DeviceStateMapper.ts +2 -2
  35. package/src/deviceProfile/buildDeviceFeatures.ts +2 -7
  36. package/src/index.ts +6 -0
  37. package/src/protocols/protocol-v2/resources.ts +12 -16
  38. package/src/types/api/firmwareUpdate.ts +1 -1
  39. package/src/utils/deviceFeaturesCompat.ts +4 -9
  40. package/src/utils/deviceInfoUtils.ts +1 -3
@@ -1 +1 @@
1
- {"version":3,"file":"deviceInfoUtils.d.ts","sourceRoot":"","sources":["../../src/utils/deviceInfoUtils.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAKlE,eAAO,MAAM,aAAa,cAAe,mBAAmB,KAAG,WAClC,CAAC;AAM9B,eAAO,MAAM,sBAAsB,UAAW,MAAM,KAAG,WAqBtD,CAAC;AAMF,eAAO,MAAM,gBAAgB,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAC3C,CAAC;AAKjC,eAAO,MAAM,iBAAiB,cAAe,mBAAmB,KAAG,MAClC,CAAC;AAKlC,eAAO,MAAM,aAAa,cANmB,mBAAmB,KAAG,MAMrB,CAAC;AAK/C,eAAO,MAAM,cAAc,cAAe,mBAAmB,kBAkB5D,CAAC;AAMF,eAAO,MAAM,qBAAqB,aACtB,mBAAmB,GAAG,SAAS,iCACV,WAAW,GAAG,YAAY,KAAK,aAAa,GAAG,SAAS,KACtF,aAAa,GAAG,SAyBlB,CAAC;AAEF,eAAO,MAAM,eAAe,cAAe,mBAAmB,gDACzB,CAAC"}
1
+ {"version":3,"file":"deviceInfoUtils.d.ts","sourceRoot":"","sources":["../../src/utils/deviceInfoUtils.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAKlE,eAAO,MAAM,aAAa,cAAe,mBAAmB,KAAG,WAClC,CAAC;AAM9B,eAAO,MAAM,sBAAsB,UAAW,MAAM,KAAG,WAmBtD,CAAC;AAMF,eAAO,MAAM,gBAAgB,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAC3C,CAAC;AAKjC,eAAO,MAAM,iBAAiB,cAAe,mBAAmB,KAAG,MAClC,CAAC;AAKlC,eAAO,MAAM,aAAa,cANmB,mBAAmB,KAAG,MAMrB,CAAC;AAK/C,eAAO,MAAM,cAAc,cAAe,mBAAmB,kBAkB5D,CAAC;AAMF,eAAO,MAAM,qBAAqB,aACtB,mBAAmB,GAAG,SAAS,iCACV,WAAW,GAAG,YAAY,KAAK,aAAa,GAAG,SAAS,KACtF,aAAa,GAAG,SAyBlB,CAAC;AAEF,eAAO,MAAM,eAAe,cAAe,mBAAmB,gDACzB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-core",
3
- "version": "1.2.0-alpha.162",
3
+ "version": "1.2.0-alpha.164",
4
4
  "description": "Core processes and APIs for communicating with OneKey hardware devices.",
5
5
  "author": "OneKey",
6
6
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
@@ -25,8 +25,8 @@
25
25
  "url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
26
26
  },
27
27
  "dependencies": {
28
- "@onekeyfe/hd-shared": "1.2.0-alpha.162",
29
- "@onekeyfe/hd-transport": "1.2.0-alpha.162",
28
+ "@onekeyfe/hd-shared": "1.2.0-alpha.164",
29
+ "@onekeyfe/hd-transport": "1.2.0-alpha.164",
30
30
  "axios": "1.15.2",
31
31
  "bignumber.js": "^9.0.2",
32
32
  "buffer": "^6.0.3",
@@ -46,5 +46,5 @@
46
46
  "@types/w3c-web-usb": "^1.0.10",
47
47
  "@types/web-bluetooth": "^0.0.21"
48
48
  },
49
- "gitHead": "f4aa654b350931d68ed9ba3f77c4cc8faa161218"
49
+ "gitHead": "8a6f829b0ea1544d2c5f7a1fd0cc0f08d6df7dcd"
50
50
  }
@@ -32,13 +32,11 @@ import {
32
32
  } from '../protocols/protocol-v2';
33
33
  import { requestProtocolV2DeviceInfo } from '../protocols/protocol-v2/features';
34
34
  import {
35
- PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH,
36
35
  PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH,
37
36
  PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_SIZE,
38
- type ProtocolV2ResourcePackageHeader,
39
- isProtocolV2ResourceArchiveEntryName,
40
37
  parseProtocolV2ResourcePackage,
41
38
  parseProtocolV2ResourcePackageHeader,
39
+ type ProtocolV2ResourcePackageHeader,
42
40
  } from '../protocols/protocol-v2/resources';
43
41
  import {
44
42
  getProtocolV2UnknownErrorText,
@@ -49,12 +47,20 @@ import {
49
47
  readFirmwareByteSourceFully,
50
48
  writeFirmwareByteSource,
51
49
  } from './firmware/FirmwareArtifactSource';
52
- import { resolveFirmwareUpdateHostBinding } from './firmware/FirmwareHostBinding';
50
+ import {
51
+ registerFirmwareUpdateHostBinding,
52
+ resolveFirmwareUpdateHostBinding,
53
+ unregisterFirmwareUpdateHostBinding,
54
+ } from './firmware/FirmwareHostBinding';
53
55
  import {
54
56
  assertFirmwareUpdatePreparedPlanBinding,
55
57
  assertFirmwareUpdatePreparedPlanDeviceIdentity,
58
+ prepareFirmwareUpdatePlan,
56
59
  validateFirmwareUpdatePreparedPlan,
57
60
  } from './firmware/FirmwareUpdatePreparedPlan';
61
+ import { prepareFirmwareUpdateV4MemoryHost } from './firmware/FirmwareMemoryHost';
62
+ import { buildProtocolV2LocalFirmwareUpdatePlan } from './firmware/FirmwareUpdatePlan';
63
+
58
64
  import type {
59
65
  FirmwareArtifactReference,
60
66
  FirmwareUpdateV4Params,
@@ -71,6 +77,11 @@ import type {
71
77
  KnownDevice,
72
78
  } from '../types';
73
79
  import type { FirmwareByteSource } from './firmware/FirmwareArtifactSource';
80
+ import type {
81
+ FirmwareMemoryArtifact,
82
+ FirmwareMemoryArtifactEntry,
83
+ FirmwareUpdateV4MemoryHost,
84
+ } from './firmware/FirmwareMemoryHost';
74
85
 
75
86
  const Log = getLogger(LoggerNames.Method);
76
87
 
@@ -219,6 +230,7 @@ type ProtocolV2ResourceBundleSource = {
219
230
 
220
231
  type ProtocolV2LocalResourceArchive = {
221
232
  binary: ArrayBuffer;
233
+ materializedEntries: FirmwareMemoryArtifactEntry[];
222
234
  resources: Array<{
223
235
  entryName: string;
224
236
  binary: ArrayBuffer;
@@ -780,7 +792,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
780
792
  this.params.resourceArchiveBinary &&
781
793
  this.params.targetsToUpdate?.includes('resource')
782
794
  ) {
783
- return this.runProtocolV2DirectArtifacts({});
795
+ const localMemoryHost = await this.prepareProtocolV2LocalMemoryHost({
796
+ features: deviceFeatures,
797
+ firmwareType,
798
+ });
799
+ try {
800
+ return await this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
801
+ } finally {
802
+ localMemoryHost.release();
803
+ }
784
804
  }
785
805
 
786
806
  const hasPreparedComponentArtifacts = Object.values(this.params.componentArtifacts ?? {}).some(
@@ -793,12 +813,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
793
813
  let fwBinaryMap: ProtocolV2TargetBinary[] = [];
794
814
  let bootloaderBinary: ArrayBuffer | null = null;
795
815
  let installItems: ProtocolV2InstallItem[] | undefined;
796
- let explicitInstallItems: ProtocolV2InstallItem[] | undefined;
816
+ let resourceMemoryHost: FirmwareUpdateV4MemoryHost | undefined;
797
817
  try {
798
818
  this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
799
819
  fwBinaryMap = this.collectExplicitTargetBinaries();
800
820
  bootloaderBinary = this.prepareBootloaderBinary();
801
- explicitInstallItems = this.buildProtocolV2InstallItems({
821
+ const explicitInstallItems = this.buildProtocolV2InstallItems({
802
822
  bootloaderBinary,
803
823
  fwBinaryMap,
804
824
  });
@@ -854,9 +874,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
854
874
  deviceFeatures,
855
875
  firmwareType
856
876
  );
877
+ resourceMemoryHost = await this.prepareProtocolV2LocalMemoryHost({
878
+ features: deviceFeatures,
879
+ firmwareType,
880
+ availableInstallItems: installItems ?? explicitInstallItems,
881
+ });
857
882
  }
858
883
  this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
859
884
  } catch (err) {
885
+ resourceMemoryHost?.release();
860
886
  if (
861
887
  typeof err === 'object' &&
862
888
  err !== null &&
@@ -871,11 +897,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
871
897
  throw normalizeFirmwarePreparationError(err);
872
898
  }
873
899
 
874
- if (wantsResources && this.params.resourceArchiveBinary) {
875
- return this.runProtocolV2DirectArtifacts({
876
- availableInstallItems: installItems ?? explicitInstallItems,
877
- announceDownload: false,
878
- });
900
+ if (resourceMemoryHost) {
901
+ try {
902
+ return await this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType, false);
903
+ } finally {
904
+ resourceMemoryHost.release();
905
+ }
879
906
  }
880
907
 
881
908
  if (!bootloaderBinary && fwBinaryMap.length === 0 && !installItems?.length) {
@@ -1028,69 +1055,133 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1028
1055
  return installSources;
1029
1056
  }
1030
1057
 
1031
- private async runProtocolV2DirectArtifacts({
1032
- availableInstallItems,
1033
- announceDownload = true,
1058
+ private async prepareProtocolV2LocalMemoryHost({
1059
+ features,
1060
+ firmwareType,
1061
+ availableInstallItems = this.buildProtocolV2InstallItems({
1062
+ bootloaderBinary: this.prepareBootloaderBinary(),
1063
+ fwBinaryMap: this.collectExplicitTargetBinaries(),
1064
+ }),
1034
1065
  }: {
1066
+ features: Features;
1067
+ firmwareType: EFirmwareType;
1035
1068
  availableInstallItems?: ProtocolV2InstallItem[];
1036
- announceDownload?: boolean;
1037
- }) {
1038
- try {
1039
- if (announceDownload) {
1040
- this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
1041
- }
1042
- const installItems = this.filterProtocolV2LocalInstallItems(
1043
- availableInstallItems ??
1044
- this.buildProtocolV2InstallItems({
1045
- bootloaderBinary: this.prepareBootloaderBinary(),
1046
- fwBinaryMap: this.collectExplicitTargetBinaries(),
1047
- })
1048
- );
1049
- const installSources = await Promise.all(
1050
- installItems.map(async item => ({
1051
- fileName: item.fileName,
1052
- source: await this.openProtocolV2MemorySource(item.binary),
1053
- targetId: item.targetId,
1054
- kind: item.kind,
1055
- }))
1069
+ }): Promise<FirmwareUpdateV4MemoryHost> {
1070
+ const requestedComponentTargets = new Set(
1071
+ (this.params.targetsToUpdate ?? []).filter(
1072
+ (target): target is Exclude<FirmwareUpdateV4Target, 'resource' | 'boot_resources'> =>
1073
+ target !== 'resource' && target !== 'boot_resources'
1074
+ )
1075
+ );
1076
+ const localComponentTargets = new Set(
1077
+ availableInstallItems.flatMap(item => {
1078
+ const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
1079
+ return target ? [target] : [];
1080
+ })
1081
+ );
1082
+ const missingTarget = Array.from(requestedComponentTargets).find(
1083
+ target => !localComponentTargets.has(target)
1084
+ );
1085
+ if (missingTarget) {
1086
+ throw ERRORS.TypedError(
1087
+ HardwareErrorCode.RuntimeError,
1088
+ `Protocol V2 local update has no binary for requested target ${missingTarget}`,
1089
+ {
1090
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
1091
+ artifactName: missingTarget,
1092
+ }
1056
1093
  );
1057
- const resourceSources = this.params.resourceArchiveBinary
1058
- ? await this.createProtocolV2ResourceSourcesFromArchive(this.params.resourceArchiveBinary)
1059
- : [];
1060
- if (installSources.length === 0 && resourceSources.length === 0) {
1094
+ }
1095
+ const installItems = this.filterProtocolV2LocalInstallItems(availableInstallItems);
1096
+
1097
+ const planArtifacts: Parameters<typeof buildProtocolV2LocalFirmwareUpdatePlan>[0]['artifacts'] =
1098
+ [];
1099
+ const memoryArtifacts: FirmwareMemoryArtifact[] = [];
1100
+ for (const item of installItems) {
1101
+ const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
1102
+ if (!target || item.binary.byteLength <= 0) {
1061
1103
  throw ERRORS.TypedError(
1062
- HardwareErrorCode.FirmwareUpdateDownloadFailed,
1063
- 'No firmware to update'
1104
+ HardwareErrorCode.RuntimeError,
1105
+ `Protocol V2 local firmware artifact is invalid: ${item.fileName}`,
1106
+ { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
1064
1107
  );
1065
1108
  }
1066
- if (announceDownload) {
1067
- this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
1068
- }
1069
- return await this.executeProtocolV2SourceUpdate({
1070
- installSources,
1071
- resourceSources,
1109
+ const artifactId = `component:${target}`;
1110
+ planArtifacts.push({
1111
+ artifactId,
1112
+ target,
1113
+ container: 'raw',
1114
+ logicalName: item.fileName,
1115
+ expectedSize: item.binary.byteLength,
1116
+ expectedSha256: bytesToHex(sha256(new Uint8Array(item.binary))),
1117
+ ...(this.params.expectedTargetVersions?.[target]
1118
+ ? { targetVersion: this.params.expectedTargetVersions[target] }
1119
+ : {}),
1072
1120
  });
1073
- } finally {
1074
- await this.closeProtocolV2PreparedSources();
1121
+ memoryArtifacts.push({ artifactId, binary: item.binary });
1075
1122
  }
1076
- }
1077
1123
 
1078
- private async createProtocolV2ResourceSourcesFromArchive(
1079
- binary: ArrayBuffer
1080
- ): Promise<ProtocolV2ResourceBundleSource[]> {
1081
- const archive = await this.prepareProtocolV2LocalResourceArchive(binary);
1082
- const sources: ProtocolV2ResourceBundleSource[] = [];
1083
- for (const resource of archive.resources) {
1084
- sources.push({
1085
- name: resource.entryName.split('/').pop() ?? resource.entryName,
1086
- source: await this.openProtocolV2MemorySource(resource.binary),
1087
- devicePath: resource.header.devicePath,
1088
- version: resource.header.version,
1089
- payloadHash: resource.header.payloadHash,
1090
- headerHash: resource.header.headerHash,
1124
+ const resourceArchive = await this.prepareProtocolV2LocalResourceArchive(
1125
+ this.params.resourceArchiveBinary as ArrayBuffer
1126
+ );
1127
+ const resourceArtifactId = 'resource:archive';
1128
+ planArtifacts.push({
1129
+ artifactId: resourceArtifactId,
1130
+ target: 'resource',
1131
+ container: 'zip',
1132
+ logicalName: 'protocol-v2-local-resource-archive',
1133
+ expectedSize: resourceArchive.binary.byteLength,
1134
+ expectedSha256: bytesToHex(sha256(new Uint8Array(resourceArchive.binary))),
1135
+ });
1136
+ memoryArtifacts.push({
1137
+ artifactId: resourceArtifactId,
1138
+ binary: resourceArchive.binary,
1139
+ materializedEntries: resourceArchive.materializedEntries,
1140
+ });
1141
+
1142
+ const plan = buildProtocolV2LocalFirmwareUpdatePlan({
1143
+ features,
1144
+ firmwareType,
1145
+ platform: this.params.platform,
1146
+ artifacts: planArtifacts,
1147
+ });
1148
+ let memoryHost: FirmwareUpdateV4MemoryHost | undefined;
1149
+ try {
1150
+ memoryHost = prepareFirmwareUpdateV4MemoryHost({
1151
+ sdk: {
1152
+ prepareFirmwareUpdatePlan,
1153
+ registerFirmwareUpdateHostBinding,
1154
+ unregisterFirmwareUpdateHostBinding,
1155
+ },
1156
+ plan,
1157
+ artifacts: memoryArtifacts,
1091
1158
  });
1159
+ const preparedPlan = validateFirmwareUpdatePreparedPlan(memoryHost.preparedPlan);
1160
+ assertFirmwareUpdatePreparedPlanBinding({
1161
+ preparedPlan,
1162
+ executor: 'v4',
1163
+ platform: this.params.platform,
1164
+ scopeTargets: [],
1165
+ bindings: [],
1166
+ });
1167
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
1168
+ preparedPlan,
1169
+ deviceIdentity: this.protocolV2ExpectedSerialNumber,
1170
+ deviceModel: this.getProtocolV2PreparedPlanDeviceModel(features),
1171
+ });
1172
+ const hostBinding = resolveFirmwareUpdateHostBinding(
1173
+ memoryHost.hostBindingGeneration,
1174
+ preparedPlan.preparedPlanDigest
1175
+ );
1176
+ this.params.preparedPlan = preparedPlan;
1177
+ this.params.targetsToUpdate = [...preparedPlan.targetsToUpdate] as FirmwareUpdateV4Target[];
1178
+ this.params.artifactReader = hostBinding.artifactReader;
1179
+ this.params.componentArtifacts = undefined;
1180
+ return memoryHost;
1181
+ } catch (error) {
1182
+ memoryHost?.release();
1183
+ throw error;
1092
1184
  }
1093
- return sources;
1094
1185
  }
1095
1186
 
1096
1187
  private async prepareProtocolV2LocalResourceArchive(
@@ -1115,7 +1206,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1115
1206
  }
1116
1207
  const zipEntries = Object.values(zip.files);
1117
1208
  const resourceEntries = zipEntries.filter(
1118
- entry => !entry.dir && isProtocolV2ResourceArchiveEntryName(entry.name)
1209
+ entry => !entry.dir && entry.name.toLowerCase().endsWith('.okpkg')
1119
1210
  );
1120
1211
  if (
1121
1212
  resourceEntries.length === 0 ||
@@ -1129,6 +1220,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1129
1220
  }
1130
1221
 
1131
1222
  let totalSize = 0;
1223
+ const materializedEntries: FirmwareMemoryArtifactEntry[] = [];
1132
1224
  const resources: ProtocolV2LocalResourceArchive['resources'] = [];
1133
1225
  const devicePaths = new Set<string>();
1134
1226
  for (const entry of resourceEntries) {
@@ -1172,9 +1264,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1172
1264
  );
1173
1265
  }
1174
1266
  devicePaths.add(canonicalDevicePath);
1267
+ materializedEntries.push({ entryName: entry.name, binary: fileBinary });
1175
1268
  resources.push({ entryName: entry.name, binary: fileBinary, header });
1176
1269
  }
1177
- return { binary, resources };
1270
+ return { binary, materializedEntries, resources };
1178
1271
  }
1179
1272
 
1180
1273
  private async prepareProtocolV2ResourceSources(): Promise<ProtocolV2ResourceBundleSource[]> {
@@ -1241,7 +1334,20 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1241
1334
 
1242
1335
  // The verified ZIP bytes are authoritative. Non-okpkg entries are release
1243
1336
  // metadata only; each RESOURCE package carries its own device path.
1244
- return this.createProtocolV2ResourceSourcesFromArchive(archiveBinary);
1337
+ const verifiedArchive = await this.prepareProtocolV2LocalResourceArchive(archiveBinary);
1338
+ const sources: ProtocolV2ResourceBundleSource[] = [];
1339
+ for (const resource of verifiedArchive.resources) {
1340
+ const source = await this.openProtocolV2MemorySource(resource.binary);
1341
+ sources.push({
1342
+ name: resource.entryName.split('/').pop() ?? resource.entryName,
1343
+ source,
1344
+ devicePath: resource.header.devicePath,
1345
+ version: resource.header.version,
1346
+ payloadHash: resource.header.payloadHash,
1347
+ headerHash: resource.header.headerHash,
1348
+ });
1349
+ }
1350
+ return sources;
1245
1351
  }
1246
1352
 
1247
1353
  private async runProtocolV2PreparedArtifacts(
@@ -1725,12 +1831,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1725
1831
  return `vol0:/${path}`;
1726
1832
  }
1727
1833
 
1728
- private getProtocolV2ResourceComparePath(devicePath: string) {
1729
- return isProtocolV2BootResourcePackagePath(devicePath)
1730
- ? PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH
1731
- : devicePath;
1732
- }
1733
-
1734
1834
  private async readProtocolV2DeviceFileHeader(path: string, expectedSize?: number) {
1735
1835
  const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
1736
1836
  const filePath = this.getProtocolV2ResourceFilePath(path);
@@ -1793,7 +1893,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1793
1893
 
1794
1894
  try {
1795
1895
  const header = await this.readProtocolV2DeviceFileHeader(
1796
- this.getProtocolV2ResourceComparePath(bundle.devicePath),
1896
+ bundle.devicePath,
1797
1897
  bundle.source.size
1798
1898
  );
1799
1899
  if (!header) return false;
@@ -2052,7 +2152,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2052
2152
  let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
2053
2153
  const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
2054
2154
  for (const resource of resourceSources) {
2055
- if (await this.isProtocolV2ResourceBundleUpToDate(resource)) {
2155
+ // Early boot promotes this mounted package's staging file. Always replace any
2156
+ // stale staging bytes with the artifact approved by the current PreparedPlan,
2157
+ // even when the mounted final file itself is already current.
2158
+ const requiresFreshStaging = isProtocolV2BootResourcePackagePath(resource.devicePath);
2159
+ if (!requiresFreshStaging && (await this.isProtocolV2ResourceBundleUpToDate(resource))) {
2056
2160
  Log.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
2057
2161
  } else {
2058
2162
  resourcesToSync.push(resource);
@@ -1,4 +1,4 @@
1
- import { canonicalizePro2BleAdvertisementName } from '@onekeyfe/hd-shared';
1
+ import { normalizePro2FindMyAdvertisementName } from '@onekeyfe/hd-shared';
2
2
 
3
3
  import { BaseMethod } from './BaseMethod';
4
4
  import TransportManager from '../data-manager/TransportManager';
@@ -40,7 +40,7 @@ export default class SearchDevices extends BaseMethod {
40
40
  seenIds.add(lowerId);
41
41
  const rawBleName =
42
42
  device.name ?? (device as unknown as { localName?: string }).localName ?? '';
43
- const bleName = canonicalizePro2BleAdvertisementName(rawBleName);
43
+ const bleName = normalizePro2FindMyAdvertisementName(rawBleName);
44
44
  devices.push({
45
45
  ...device,
46
46
  connectId: device.id,
@@ -0,0 +1,143 @@
1
+ import { sha256 } from '@noble/hashes/sha256';
2
+ import { bytesToHex } from '@noble/hashes/utils';
3
+ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
4
+
5
+ import type { CoreApi } from '../../types/api';
6
+ import type {
7
+ FirmwareArtifactReader,
8
+ FirmwareArtifactReference,
9
+ } from '../../types/api/firmwareUpdate';
10
+ import type { FirmwareUpdatePlan } from '../../types/api/firmwareUpdatePlan';
11
+
12
+ export type FirmwareMemoryArtifactEntry = {
13
+ entryName: string;
14
+ binary: ArrayBuffer;
15
+ };
16
+
17
+ export type FirmwareMemoryArtifact = {
18
+ artifactId: string;
19
+ binary: ArrayBuffer;
20
+ materializedEntries?: FirmwareMemoryArtifactEntry[];
21
+ };
22
+
23
+ export type FirmwareUpdateV4MemoryHost = {
24
+ preparedPlan: ReturnType<CoreApi['prepareFirmwareUpdatePlan']>;
25
+ hostBindingGeneration: number;
26
+ release: () => void;
27
+ };
28
+
29
+ let memoryHostSequence = 0;
30
+
31
+ const createReference = (binary: ArrayBuffer, prefix: string): FirmwareArtifactReference => {
32
+ const digest = bytesToHex(sha256(new Uint8Array(binary)));
33
+ return {
34
+ artifactRef: `fwmem:${prefix}:${digest.slice(0, 32)}`,
35
+ size: binary.byteLength,
36
+ sha256: digest,
37
+ };
38
+ };
39
+
40
+ export function prepareFirmwareUpdateV4MemoryHost({
41
+ sdk,
42
+ plan,
43
+ artifacts,
44
+ }: {
45
+ sdk: Pick<
46
+ CoreApi,
47
+ | 'prepareFirmwareUpdatePlan'
48
+ | 'registerFirmwareUpdateHostBinding'
49
+ | 'unregisterFirmwareUpdateHostBinding'
50
+ >;
51
+ plan: FirmwareUpdatePlan;
52
+ artifacts: FirmwareMemoryArtifact[];
53
+ }): FirmwareUpdateV4MemoryHost {
54
+ if (plan.executor !== 'v4') {
55
+ throw ERRORS.TypedError(
56
+ HardwareErrorCode.RuntimeError,
57
+ 'Firmware memory host only supports V4 plans'
58
+ );
59
+ }
60
+ memoryHostSequence += 1;
61
+ const hostId = `${Date.now()}:${memoryHostSequence}`;
62
+ const binaries = new Map<string, Uint8Array>();
63
+ const inputs = artifacts.map((input, artifactIndex) => {
64
+ const artifactBinary = new Uint8Array(input.binary).slice();
65
+ const artifact = createReference(
66
+ artifactBinary.buffer as ArrayBuffer,
67
+ `${hostId}:artifact:${artifactIndex}`
68
+ );
69
+ binaries.set(artifact.artifactRef, artifactBinary);
70
+ const materializedEntries = input.materializedEntries?.map((entry, entryIndex) => {
71
+ const entryArtifact = createReference(
72
+ entry.binary,
73
+ `${hostId}:entry:${artifactIndex}:${entryIndex}`
74
+ );
75
+ // Entry references are compact receipts for bytes that will be re-derived
76
+ // from the verified archive. Retaining another readable copy here doubles
77
+ // resource memory without adding an execution trust boundary.
78
+ return {
79
+ entryName: entry.entryName,
80
+ artifact: entryArtifact,
81
+ };
82
+ });
83
+ return {
84
+ artifactId: input.artifactId,
85
+ artifact,
86
+ ...(materializedEntries?.length ? { materializedEntries } : {}),
87
+ };
88
+ });
89
+ const preparedPlan = sdk.prepareFirmwareUpdatePlan({
90
+ plan,
91
+ leaseRef: `fwmemlease:${hostId}`,
92
+ artifacts: inputs,
93
+ });
94
+ const readers = new Map<string, Uint8Array>();
95
+ let readerSequence = 0;
96
+ const artifactReader: FirmwareArtifactReader = {
97
+ open({ artifactRef }) {
98
+ const binary = binaries.get(artifactRef);
99
+ if (!binary) {
100
+ throw ERRORS.TypedError(
101
+ HardwareErrorCode.RuntimeError,
102
+ 'Firmware memory artifact is unavailable'
103
+ );
104
+ }
105
+ readerSequence += 1;
106
+ const readerId = `fwmemreader:${hostId}:${readerSequence}`;
107
+ readers.set(readerId, binary);
108
+ return Promise.resolve({ readerId, size: binary.byteLength });
109
+ },
110
+ read({ readerId, offset, length }) {
111
+ const binary = readers.get(readerId);
112
+ if (!binary || offset < 0 || length <= 0 || offset + length > binary.byteLength) {
113
+ throw ERRORS.TypedError(
114
+ HardwareErrorCode.RuntimeError,
115
+ 'Firmware memory artifact read is invalid'
116
+ );
117
+ }
118
+ const data = binary.slice(offset, offset + length).buffer;
119
+ return Promise.resolve({
120
+ data,
121
+ bytesRead: data.byteLength,
122
+ eof: offset + length === binary.byteLength,
123
+ });
124
+ },
125
+ close({ readerId }) {
126
+ readers.delete(readerId);
127
+ return Promise.resolve();
128
+ },
129
+ };
130
+ const hostBindingGeneration = sdk.registerFirmwareUpdateHostBinding({
131
+ artifactReader,
132
+ preparedPlanDigest: preparedPlan.preparedPlanDigest,
133
+ });
134
+ return {
135
+ preparedPlan,
136
+ hostBindingGeneration,
137
+ release: () => {
138
+ sdk.unregisterFirmwareUpdateHostBinding(hostBindingGeneration);
139
+ readers.clear();
140
+ binaries.clear();
141
+ },
142
+ };
143
+ }
@@ -654,6 +654,47 @@ const finalizeFirmwareUpdatePlan = ({
654
654
  });
655
655
  };
656
656
 
657
+ export type ProtocolV2LocalFirmwareUpdatePlanArtifact = {
658
+ artifactId: string;
659
+ target: Exclude<FirmwareUpdateV4Target, 'boot_resources'>;
660
+ container: 'raw' | 'zip';
661
+ logicalName: string;
662
+ expectedSize: number;
663
+ expectedSha256: string;
664
+ targetVersion?: string;
665
+ };
666
+
667
+ export const buildProtocolV2LocalFirmwareUpdatePlan = ({
668
+ features,
669
+ firmwareType,
670
+ platform,
671
+ artifacts,
672
+ }: {
673
+ features: Features;
674
+ firmwareType: EFirmwareType;
675
+ platform: FirmwareUpdatePlatform;
676
+ artifacts: ProtocolV2LocalFirmwareUpdatePlanArtifact[];
677
+ }): FirmwareUpdatePlan => {
678
+ if (artifacts.length === 0) {
679
+ return planError('Protocol V2 local firmware plan has no artifacts');
680
+ }
681
+ const plan = finalizeFirmwareUpdatePlan({
682
+ features,
683
+ firmwareType,
684
+ platform,
685
+ artifacts: artifacts.map(artifact => ({
686
+ ...artifact,
687
+ role: artifact.target === 'resource' ? 'resourceBundle' : 'component',
688
+ url: `https://local-firmware.invalid/${encodeURIComponent(artifact.artifactId)}`,
689
+ })),
690
+ targetsToUpdate: artifacts.map(artifact => artifact.target),
691
+ });
692
+ if (plan.executor !== 'v4') {
693
+ return planError('Protocol V2 local firmware plan requires executor v4');
694
+ }
695
+ return plan;
696
+ };
697
+
657
698
  export const buildProtocolV2FirmwareUpdatePlan = ({
658
699
  features,
659
700
  firmwareType,
@@ -71,13 +71,6 @@ export const getFirmwareUpdateResourceName = (value: unknown): string => {
71
71
  return resourceName;
72
72
  };
73
73
 
74
- const getPreparedEntryIdentity = (entryName: unknown): string => {
75
- getFirmwareUpdateResourceName(entryName);
76
- // Protocol V2 resource ZIPs can ship the same basename from different
77
- // directories, such as rom/params.okpkg and bootloader/params.okpkg.
78
- return (entryName as string).toLowerCase();
79
- };
80
-
81
74
  const assertPreparedEntry = (value: unknown): FirmwareUpdatePreparedEntry => {
82
75
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
83
76
  return preparedPlanError('Firmware prepared plan entry must be an object');
@@ -148,8 +141,11 @@ const assertPreparedArtifacts = ({
148
141
  }
149
142
  if (
150
143
  materializedEntries &&
151
- new Set(materializedEntries.map(entry => getPreparedEntryIdentity(entry.entryName))).size !==
152
- materializedEntries.length
144
+ new Set(
145
+ materializedEntries.map(entry =>
146
+ getFirmwareUpdateResourceName(entry.entryName).toLowerCase()
147
+ )
148
+ ).size !== materializedEntries.length
153
149
  ) {
154
150
  return preparedPlanError('Firmware prepared plan contains duplicate entry names');
155
151
  }
@@ -285,7 +281,7 @@ export const validateFirmwareUpdatePreparedPlan = (value: unknown): FirmwareUpda
285
281
  const materializedEntryNames =
286
282
  artifact.materializedEntries?.map(entry => {
287
283
  assertPreparedEntry(entry);
288
- return getPreparedEntryIdentity(entry.entryName);
284
+ return getFirmwareUpdateResourceName(entry.entryName).toLowerCase();
289
285
  }) ?? [];
290
286
  if (new Set(materializedEntryNames).size !== materializedEntryNames.length) {
291
287
  return preparedPlanError('Firmware prepared plan contains duplicate entry names');