@onekeyfe/hd-core 1.2.0-alpha.27 → 1.2.0-alpha.28
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.
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +17 -1
- package/__tests__/firmware-update/firmware-preparation-error.test.ts +27 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +169 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +20 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/firmware/FirmwarePreparationError.d.ts +3 -0
- package/dist/api/firmware/FirmwarePreparationError.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +1 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
- package/dist/index.js +85 -19
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +7 -2
- package/src/api/FirmwareUpdateV2.ts +3 -5
- package/src/api/FirmwareUpdateV3.ts +3 -2
- package/src/api/FirmwareUpdateV4.ts +2 -1
- package/src/api/firmware/FirmwarePreparationError.ts +12 -0
- package/src/api/firmware/FirmwareUpdatePlan.ts +119 -12
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EDeviceType, EFirmwareType, ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import CheckAllFirmwareRelease from '../../src/api/CheckAllFirmwareRelease';
|
|
4
4
|
import { buildFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePlan';
|
|
@@ -15,6 +15,7 @@ jest.mock('../../src/data/config', () => ({
|
|
|
15
15
|
|
|
16
16
|
jest.mock('../../src/api/firmware/FirmwareUpdatePlan', () => ({
|
|
17
17
|
buildFirmwareUpdatePlan: jest.fn(),
|
|
18
|
+
validateFirmwareUpdatePlanForceTargets: jest.fn(value => value ?? []),
|
|
18
19
|
}));
|
|
19
20
|
|
|
20
21
|
jest.mock('../../src/api/firmware/releaseHelper', () => ({
|
|
@@ -147,6 +148,21 @@ describe('CheckAllFirmwareRelease', () => {
|
|
|
147
148
|
);
|
|
148
149
|
});
|
|
149
150
|
|
|
151
|
+
test('does not hide a Plan failure for an explicitly forced target', async () => {
|
|
152
|
+
const planError = ERRORS.TypedError(
|
|
153
|
+
HardwareErrorCode.RuntimeError,
|
|
154
|
+
'Forced firmware target is unavailable',
|
|
155
|
+
{
|
|
156
|
+
firmwareUpdateCode: 'FirmwarePlanInvalid',
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
mockBuildFirmwareUpdatePlan.mockImplementation(() => {
|
|
160
|
+
throw planError;
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
await expect(createMethodWithForceTargets().run()).rejects.toBe(planError);
|
|
164
|
+
});
|
|
165
|
+
|
|
150
166
|
test('does not hide unexpected Plan builder failures', async () => {
|
|
151
167
|
const unexpectedError = new Error('unexpected builder failure');
|
|
152
168
|
mockBuildFirmwareUpdatePlan.mockImplementation(() => {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import { normalizeFirmwarePreparationError } from '../../src/api/firmware/FirmwarePreparationError';
|
|
4
|
+
|
|
5
|
+
describe('normalizeFirmwarePreparationError', () => {
|
|
6
|
+
test('preserves structured firmware invariant errors', () => {
|
|
7
|
+
const error = ERRORS.TypedError(
|
|
8
|
+
HardwareErrorCode.RuntimeError,
|
|
9
|
+
'Firmware must be prepared by the external firmware host',
|
|
10
|
+
{
|
|
11
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
12
|
+
artifactName: 'firmware',
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
expect(normalizeFirmwarePreparationError(error)).toBe(error);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('wraps ordinary preparation failures as firmware download errors', () => {
|
|
20
|
+
const error = normalizeFirmwarePreparationError(new Error('request failed'));
|
|
21
|
+
|
|
22
|
+
expect(error).toMatchObject({
|
|
23
|
+
errorCode: HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
24
|
+
message: 'request failed',
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -25,6 +25,63 @@ const noUpdate = {
|
|
|
25
25
|
release: undefined,
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
const expectFirmwarePlanInvalid = (build: () => unknown, expectedMessage?: string) => {
|
|
29
|
+
let thrown: unknown;
|
|
30
|
+
try {
|
|
31
|
+
build();
|
|
32
|
+
} catch (error) {
|
|
33
|
+
thrown = error;
|
|
34
|
+
}
|
|
35
|
+
expect(thrown).toMatchObject({
|
|
36
|
+
params: {
|
|
37
|
+
firmwareUpdateCode: 'FirmwarePlanInvalid',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
if (expectedMessage) {
|
|
41
|
+
expect(thrown).toBeInstanceOf(Error);
|
|
42
|
+
expect((thrown as Error).message).toContain(expectedMessage);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
type BuildPlanInput = Parameters<typeof buildFirmwareUpdatePlan>[0];
|
|
47
|
+
|
|
48
|
+
const createLegacyForceInput = (
|
|
49
|
+
forceUpdateTargets: unknown,
|
|
50
|
+
release: Record<string, unknown> = {
|
|
51
|
+
url: 'https://firmware.onekey.so/pro/firmware.bin',
|
|
52
|
+
version: [4, 21, 0],
|
|
53
|
+
}
|
|
54
|
+
): BuildPlanInput => ({
|
|
55
|
+
features: createFeatures({
|
|
56
|
+
deviceType: EDeviceType.Pro,
|
|
57
|
+
firmwareVersion: '4.21.0',
|
|
58
|
+
bootloaderVersion: '2.8.4',
|
|
59
|
+
}),
|
|
60
|
+
firmwareType: EFirmwareType.Universal,
|
|
61
|
+
platform: 'desktop',
|
|
62
|
+
firmware: { status: 'valid', release },
|
|
63
|
+
ble: noUpdate,
|
|
64
|
+
bootloader: noUpdate,
|
|
65
|
+
forceUpdateTargets: forceUpdateTargets as BuildPlanInput['forceUpdateTargets'],
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const createProtocolV2ForceInput = (
|
|
69
|
+
forceUpdateTargets: unknown,
|
|
70
|
+
release: Record<string, unknown>
|
|
71
|
+
): BuildPlanInput => ({
|
|
72
|
+
features: createFeatures({
|
|
73
|
+
deviceType: EDeviceType.Pro2,
|
|
74
|
+
firmwareVersion: '1.0.0',
|
|
75
|
+
bootloaderVersion: '1.0.0',
|
|
76
|
+
}),
|
|
77
|
+
firmwareType: EFirmwareType.Universal,
|
|
78
|
+
platform: 'desktop',
|
|
79
|
+
firmware: { status: 'valid', release },
|
|
80
|
+
ble: noUpdate,
|
|
81
|
+
bootloader: noUpdate,
|
|
82
|
+
forceUpdateTargets: forceUpdateTargets as BuildPlanInput['forceUpdateTargets'],
|
|
83
|
+
});
|
|
84
|
+
|
|
28
85
|
describe('buildFirmwareUpdatePlan', () => {
|
|
29
86
|
test('selects all legacy artifacts before execution and chooses the desktop full resource', () => {
|
|
30
87
|
const plan = buildFirmwareUpdatePlan({
|
|
@@ -192,6 +249,118 @@ describe('buildFirmwareUpdatePlan', () => {
|
|
|
192
249
|
expect(plan.targetsToUpdate).toEqual(['firmware']);
|
|
193
250
|
});
|
|
194
251
|
|
|
252
|
+
test.each([
|
|
253
|
+
{
|
|
254
|
+
label: 'a non-array value',
|
|
255
|
+
forceUpdateTargets: 'firmware',
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
label: 'an unknown target',
|
|
259
|
+
forceUpdateTargets: ['firmware', 'unknown'],
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
label: 'a duplicate target',
|
|
263
|
+
forceUpdateTargets: ['firmware', 'firmware'],
|
|
264
|
+
},
|
|
265
|
+
])('rejects $label in forceUpdateTargets', ({ forceUpdateTargets }) => {
|
|
266
|
+
expectFirmwarePlanInvalid(() =>
|
|
267
|
+
buildFirmwareUpdatePlan(createLegacyForceInput(forceUpdateTargets))
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('rejects an explicitly forced legacy resource without a resource artifact', () => {
|
|
272
|
+
expectFirmwarePlanInvalid(() => buildFirmwareUpdatePlan(createLegacyForceInput(['resource'])));
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test.each(['ble', 'bootloader'] as const)(
|
|
276
|
+
'rejects the unsupported Pro2 %s force target',
|
|
277
|
+
forceTarget => {
|
|
278
|
+
expectFirmwarePlanInvalid(
|
|
279
|
+
() =>
|
|
280
|
+
buildFirmwareUpdatePlan(
|
|
281
|
+
createProtocolV2ForceInput([forceTarget], {
|
|
282
|
+
components: {
|
|
283
|
+
applicationP1: {
|
|
284
|
+
target: 'APPLICATION_P1',
|
|
285
|
+
url: 'https://firmware.onekey.so/pro2/application-p1.bin',
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
})
|
|
289
|
+
),
|
|
290
|
+
'does not support forced'
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
test.each([
|
|
296
|
+
{
|
|
297
|
+
forceTarget: 'firmware' as const,
|
|
298
|
+
release: {
|
|
299
|
+
components: {},
|
|
300
|
+
resourceBundles: [
|
|
301
|
+
{
|
|
302
|
+
name: 'images',
|
|
303
|
+
url: 'https://firmware.onekey.so/pro2/images.okpkg',
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
forceTarget: 'resource' as const,
|
|
310
|
+
release: {
|
|
311
|
+
components: {
|
|
312
|
+
applicationP1: {
|
|
313
|
+
target: 'APPLICATION_P1',
|
|
314
|
+
url: 'https://firmware.onekey.so/pro2/application-p1.bin',
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
])(
|
|
320
|
+
'rejects a Pro2 $forceTarget force target not represented by an artifact',
|
|
321
|
+
({ forceTarget, release }) => {
|
|
322
|
+
expectFirmwarePlanInvalid(() =>
|
|
323
|
+
buildFirmwareUpdatePlan(createProtocolV2ForceInput([forceTarget], release))
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
test.each([
|
|
329
|
+
{
|
|
330
|
+
forceTarget: 'firmware' as const,
|
|
331
|
+
expectedArtifactIds: ['component:app_v1'],
|
|
332
|
+
expectedTargets: ['app_v1'],
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
forceTarget: 'resource' as const,
|
|
336
|
+
expectedArtifactIds: ['resourceBundle:images'],
|
|
337
|
+
expectedTargets: ['resource'],
|
|
338
|
+
},
|
|
339
|
+
])(
|
|
340
|
+
'limits a Pro2 $forceTarget force to the selected artifact role',
|
|
341
|
+
({ forceTarget, expectedArtifactIds, expectedTargets }) => {
|
|
342
|
+
const plan = buildFirmwareUpdatePlan(
|
|
343
|
+
createProtocolV2ForceInput([forceTarget], {
|
|
344
|
+
components: {
|
|
345
|
+
applicationP1: {
|
|
346
|
+
target: 'APPLICATION_P1',
|
|
347
|
+
url: 'https://firmware.onekey.so/pro2/application-p1.bin',
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
resourceBundles: [
|
|
351
|
+
{
|
|
352
|
+
name: 'images',
|
|
353
|
+
url: 'https://firmware.onekey.so/pro2/images.okpkg',
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
})
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
expect(plan.artifacts.map(artifact => artifact.artifactId)).toEqual(expectedArtifactIds);
|
|
360
|
+
expect(plan.targetsToUpdate).toEqual(expectedTargets);
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
|
|
195
364
|
test('rejects a prepared native plan without a stable device identity', () => {
|
|
196
365
|
const features = createFeatures({
|
|
197
366
|
deviceType: EDeviceType.Classic1s,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EDeviceType, EFirmwareType, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
1
|
+
import { EDeviceType, EFirmwareType, ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import FirmwareUpdateV2 from '../../src/api/FirmwareUpdateV2';
|
|
4
4
|
import { getBinary } from '../../src/api/firmware/getBinary';
|
|
@@ -181,6 +181,25 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
181
181
|
expect(mockUploadFirmwareFromSource).not.toHaveBeenCalled();
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
+
it('preserves structured preparation errors before any firmware mutation', async () => {
|
|
185
|
+
const preparationError = ERRORS.TypedError(
|
|
186
|
+
HardwareErrorCode.RuntimeError,
|
|
187
|
+
'Firmware must be prepared by the external firmware host',
|
|
188
|
+
{
|
|
189
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
190
|
+
artifactName: 'firmware',
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
mockGetBinary.mockRejectedValue(preparationError);
|
|
194
|
+
const { method, typedCall, acquire } = createMethod();
|
|
195
|
+
|
|
196
|
+
await expect(method.run()).rejects.toBe(preparationError);
|
|
197
|
+
|
|
198
|
+
expectNoFirmwareMutationCalls(typedCall);
|
|
199
|
+
expect(acquire).not.toHaveBeenCalled();
|
|
200
|
+
expect(mockUploadFirmwareFromSource).not.toHaveBeenCalled();
|
|
201
|
+
});
|
|
202
|
+
|
|
184
203
|
it.each([
|
|
185
204
|
{
|
|
186
205
|
mode: 'firmware release.url',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CheckAllFirmwareRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckAllFirmwareRelease.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"CheckAllFirmwareRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckAllFirmwareRelease.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAoB1C,OAAO,KAAK,EACV,kBAAkB,EAEnB,MAAM,sCAAsC,CAAC;AAK9C,MAAM,CAAC,OAAO,OAAO,uBAAwB,SAAQ,UAAU;IAC7D,IAAI;IAME,GAAG;CAkFV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmwareUpdateV2.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV2.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,aAAa,EAKnB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"FirmwareUpdateV2.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV2.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,aAAa,EAKnB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAuB1C,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,UAAU,CAAC;AAEtD,OAAO,KAAK,EACV,sBAAsB,EACtB,yBAAyB,EAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AAE1F,KAAK,MAAM,GAAG;IACZ,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;IAC7C,eAAe,CAAC,EAAE,KAAK,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,yBAAyB,CAAC;KACrC,CAAC,CAAC;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,YAAY,CAAC,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,CAAC,EAAE,0BAA0B,CAAC,UAAU,CAAC,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,UAAU,GAAG,KAAK,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B,CAAC;AAkFF,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,UAAU,CAAC,MAAM,CAAC;IAC9D,YAAY,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,CAAQ;IAE1C,IAAI;IA6EJ,cAAc,YAAa,MAAM,UAS/B;YAEY,qCAAqC;IAkBnD,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS;YAkFvC,4BAA4B;IAuB1C,qBAAqB;IAUrB,uBAAuB,CAAC,UAAU,EAAE,MAAM;IAc1C,gCAAgC,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAAE,YAAY,EAAE,aAAa;IAwBtF,GAAG;CAyQV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmwareUpdateV3.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV3.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FirmwareUpdateV3.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV3.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAY/E,OAAO,KAAK,EAEV,sBAAsB,EACvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,qBAAqB,CAAC;AAMnE,eAAO,MAAM,gCAAgC,UAAU,CAAC;AAaxD,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,wBAAwB,CAAC,sBAAsB,CAAC;IAC5F,YAAY,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,CAAQ;IAE1C,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,eAAe,CAA4B;IAEnD,IAAI;IAmFE,GAAG;;;;;YAQK,aAAa;IA6D3B,OAAO,CAAC,wBAAwB;YAiBlB,kBAAkB;YAelB,oBAAoB;IAKlC,OAAO,CAAC,wBAAwB;YAgBlB,oBAAoB;YA2DpB,uBAAuB;YAuBvB,4BAA4B;YA+E5B,aAAa;IAwN3B,OAAO,CAAC,yBAAyB;IAajC,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,qBAAqB;IAmB7B,OAAO,CAAC,sCAAsC;IAexC,sBAAsB,CAAC,OAAO,EAAE,MAAM;CAuE7C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAkB/E,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,6BAA6B,CAAC;AA4VrC,eAAO,MAAM,iCAAiC,0BACrB,MAAM,uBACR,MAAM,SAc5B,CAAC;AAUF,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,wBAAwB,CAAC,sBAAsB,CAAC;IAC5F,OAAO,CAAC,8BAA8B,CAAC,CAAS;IAEhD,qBAAqB;IAIrB,OAAO,CAAC,yBAAyB,CAA4B;IAE7D,OAAO,CAAC,2BAA2B,CAAS;IAE5C,OAAO,CAAC,iCAAiC,CAA6B;IAEtE,OAAO,CAAC,6BAA6B,CAAC,CAAW;IAEjD,OAAO,CAAC,6BAA6B,CAAS;IAE9C,IAAI;IAoHJ,OAAO,CAAC,8BAA8B;IAoBhC,GAAG;;;;;YAKK,aAAa;YAsEb,4BAA4B;YAkB5B,0BAA0B;YAY1B,8BAA8B;YAK9B,+BAA+B;YAqD/B,gCAAgC;YAmDhC,8BAA8B;IAsB5C,OAAO,CAAC,8BAA8B;IA0BtC,OAAO,CAAC,kCAAkC;IAc1C,OAAO,CAAC,gCAAgC;IAsDxC,OAAO,CAAC,6BAA6B;YAsBvB,2BAA2B;IAWzC,OAAO,CAAC,yBAAyB;IAKjC,OAAO,CAAC,kCAAkC;YAO5B,iCAAiC;YAOjC,iCAAiC;YAgBjC,iCAAiC;IAM/C,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,4BAA4B;IAQpC,OAAO,CAAC,2BAA2B;IAsBnC,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,wBAAwB;IAkBhC,OAAO,CAAC,6BAA6B;YAMvB,8BAA8B;YA6C9B,iCAAiC;YAmBjC,+BAA+B;IA+D7C,OAAO,CAAC,gCAAgC;YAyC1B,oCAAoC;YAkCpC,kCAAkC;IAgChD,OAAO,CAAC,0BAA0B;IAO5B,6BAA6B;YA0BrB,+BAA+B;IA4C7C,OAAO,CAAC,6BAA6B;IAkCrC,OAAO,CAAC,8BAA8B;YA8CxB,uBAAuB;YAiCvB,6BAA6B;YAa7B,uBAAuB;YA6CvB,8BAA8B;YA2D9B,6BAA6B;IA0D3C,OAAO,CAAC,mCAAmC;YAI7B,0BAA0B;IAaxC,OAAO,CAAC,4BAA4B;YA+FtB,uCAAuC;YA4GvC,gCAAgC;YAShC,8BAA8B;YAsB9B,mCAAmC;YAWnC,qCAAqC;YA2CrC,yBAAyB;YAiDzB,8BAA8B;IAU5C,OAAO,CAAC,kCAAkC;YAI5B,cAAc;YA+Bd,6BAA6B;YAU7B,0BAA0B;YAS1B,6BAA6B;YAmB7B,gBAAgB;IAiB9B,OAAO,CAAC,qBAAqB;CAM9B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FirmwarePreparationError.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwarePreparationError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,aAAa,EAAqB,MAAM,qBAAqB,CAAC;AAE/E,eAAO,MAAM,iCAAiC,UAAW,OAAO,kBAS/D,CAAC"}
|
|
@@ -8,6 +8,7 @@ type ReleaseSelection = {
|
|
|
8
8
|
release?: unknown;
|
|
9
9
|
};
|
|
10
10
|
export declare const digestFirmwareUpdateContract: (value: unknown) => string;
|
|
11
|
+
export declare const validateFirmwareUpdatePlanForceTargets: (value: unknown) => FirmwareUpdatePlanForceTarget[];
|
|
11
12
|
export declare const FIRMWARE_UPDATE_PLAN_TARGETS: Set<FirmwareUpdatePlanTarget>;
|
|
12
13
|
export declare const FIRMWARE_UPDATE_PLAN_ROLES: Set<string>;
|
|
13
14
|
export declare const assertFirmwareUpdatePlan: (value: unknown) => FirmwareUpdatePlan;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmwareUpdatePlan.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwareUpdatePlan.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAS5F,OAAO,KAAK,EACV,kBAAkB,EAElB,6BAA6B,EAC7B,wBAAwB,EACzB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,KAAK,sBAAsB,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,WAAW,CAAC;AAyBjF,KAAK,gBAAgB,GAAG;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AA0GF,eAAO,MAAM,4BAA4B,UAAW,OAAO,KAAG,MAG3D,CAAC;
|
|
1
|
+
{"version":3,"file":"FirmwareUpdatePlan.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwareUpdatePlan.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAS5F,OAAO,KAAK,EACV,kBAAkB,EAElB,6BAA6B,EAC7B,wBAAwB,EACzB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,KAAK,sBAAsB,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,WAAW,CAAC;AAyBjF,KAAK,gBAAgB,GAAG;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AA0GF,eAAO,MAAM,4BAA4B,UAAW,OAAO,KAAG,MAG3D,CAAC;AAkBJ,eAAO,MAAM,sCAAsC,UAC1C,OAAO,KACb,6BAA6B,EAa/B,CAAC;AAuBF,eAAO,MAAM,4BAA4B,+BAavC,CAAC;AAEH,eAAO,MAAM,0BAA0B,aAOrC,CAAC;AAYH,eAAO,MAAM,wBAAwB,UAAW,OAAO,KAAG,kBAoHzD,CAAC;AA4NF,eAAO,MAAM,uBAAuB;cASxB,QAAQ;kBACJ,aAAa;cACjB,sBAAsB;cACtB,gBAAgB;SACrB,gBAAgB;;;MAGnB,kBAiJH,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -564,6 +564,24 @@ const planError = (message) => {
|
|
|
564
564
|
firmwareUpdateCode: 'FirmwarePlanInvalid',
|
|
565
565
|
});
|
|
566
566
|
};
|
|
567
|
+
const FIRMWARE_UPDATE_PLAN_FORCE_TARGETS = new Set([
|
|
568
|
+
'firmware',
|
|
569
|
+
'ble',
|
|
570
|
+
'bootloader',
|
|
571
|
+
'resource',
|
|
572
|
+
]);
|
|
573
|
+
const validateFirmwareUpdatePlanForceTargets = (value) => {
|
|
574
|
+
if (value === undefined) {
|
|
575
|
+
return [];
|
|
576
|
+
}
|
|
577
|
+
if (!Array.isArray(value) ||
|
|
578
|
+
value.length > FIRMWARE_UPDATE_PLAN_FORCE_TARGETS.size ||
|
|
579
|
+
value.some(target => !FIRMWARE_UPDATE_PLAN_FORCE_TARGETS.has(target)) ||
|
|
580
|
+
new Set(value).size !== value.length) {
|
|
581
|
+
return planError('Firmware update force targets are invalid');
|
|
582
|
+
}
|
|
583
|
+
return [...value];
|
|
584
|
+
};
|
|
567
585
|
const assertExactKeys = (value, required, optional = []) => {
|
|
568
586
|
const allowed = new Set([...required, ...optional]);
|
|
569
587
|
if (required.some(key => !Object.prototype.hasOwnProperty.call(value, key)) ||
|
|
@@ -736,24 +754,23 @@ const getExecutor = (features) => {
|
|
|
736
754
|
}
|
|
737
755
|
return 'v2';
|
|
738
756
|
};
|
|
739
|
-
const buildProtocolV2Artifacts = (release) => {
|
|
757
|
+
const buildProtocolV2Artifacts = (release, { includeComponents = true, includeResources = true, } = {}) => {
|
|
740
758
|
var _a;
|
|
741
759
|
const components = asRecord(release.components);
|
|
742
|
-
if (!components) {
|
|
760
|
+
if (includeComponents && !components) {
|
|
743
761
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 release has no component set', { firmwareUpdateCode: 'FirmwarePlanInvalid' });
|
|
744
762
|
}
|
|
745
763
|
const installOrder = Array.isArray(release.installOrder)
|
|
746
764
|
? release.installOrder.filter((key) => typeof key === 'string')
|
|
747
765
|
: [];
|
|
748
|
-
const componentKeys =
|
|
749
|
-
...installOrder,
|
|
750
|
-
|
|
751
|
-
];
|
|
766
|
+
const componentKeys = includeComponents && components
|
|
767
|
+
? [...installOrder, ...Object.keys(components).filter(key => !installOrder.includes(key))]
|
|
768
|
+
: [];
|
|
752
769
|
const artifacts = [];
|
|
753
770
|
const targets = [];
|
|
754
771
|
const componentTargets = new Set();
|
|
755
772
|
for (const key of componentKeys) {
|
|
756
|
-
const component = asRecord(components[key]);
|
|
773
|
+
const component = asRecord(components === null || components === void 0 ? void 0 : components[key]);
|
|
757
774
|
const targetName = (_a = asString(component === null || component === void 0 ? void 0 : component.target)) === null || _a === void 0 ? void 0 : _a.toUpperCase();
|
|
758
775
|
if (targetName === 'ROMLOADER') {
|
|
759
776
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 ROMLOADER requires its dedicated loader flow', { firmwareUpdateCode: 'FirmwarePlanInvalid' });
|
|
@@ -772,7 +789,7 @@ const buildProtocolV2Artifacts = (release) => {
|
|
|
772
789
|
})), (asVersion(component.version) ? { targetVersion: asVersion(component.version) } : {})));
|
|
773
790
|
targets.push(target);
|
|
774
791
|
}
|
|
775
|
-
const bundles = Array.isArray(release.resourceBundles) ? release.resourceBundles : [];
|
|
792
|
+
const bundles = includeResources && Array.isArray(release.resourceBundles) ? release.resourceBundles : [];
|
|
776
793
|
const resourceBundleNames = new Set();
|
|
777
794
|
for (const value of bundles) {
|
|
778
795
|
const bundle = asRecord(value);
|
|
@@ -794,17 +811,51 @@ const buildProtocolV2Artifacts = (release) => {
|
|
|
794
811
|
}
|
|
795
812
|
return { artifacts, targets: [...new Set(targets)] };
|
|
796
813
|
};
|
|
814
|
+
const isForcedTargetRepresented = ({ executor, forcedTarget, artifacts, targetsToUpdate, }) => {
|
|
815
|
+
if (executor !== 'v4') {
|
|
816
|
+
return artifacts.some(artifact => artifact.target === forcedTarget && targetsToUpdate.includes(forcedTarget));
|
|
817
|
+
}
|
|
818
|
+
if (forcedTarget === 'firmware') {
|
|
819
|
+
return artifacts.some(artifact => artifact.role === 'component' && targetsToUpdate.includes(artifact.target));
|
|
820
|
+
}
|
|
821
|
+
if (forcedTarget === 'resource') {
|
|
822
|
+
return artifacts.some(artifact => artifact.role === 'resourceBundle' &&
|
|
823
|
+
artifact.target === 'resource' &&
|
|
824
|
+
targetsToUpdate.includes('resource'));
|
|
825
|
+
}
|
|
826
|
+
return false;
|
|
827
|
+
};
|
|
828
|
+
const assertForcedTargetsRepresented = ({ executor, forcedTargets, artifacts, targetsToUpdate, }) => {
|
|
829
|
+
for (const forcedTarget of forcedTargets) {
|
|
830
|
+
if (!isForcedTargetRepresented({
|
|
831
|
+
executor,
|
|
832
|
+
forcedTarget,
|
|
833
|
+
artifacts,
|
|
834
|
+
targetsToUpdate,
|
|
835
|
+
})) {
|
|
836
|
+
planError(`Forced firmware update target ${forcedTarget} is not represented by the plan`);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
};
|
|
797
840
|
const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, ble, bootloader, forceUpdateTargets, }) => {
|
|
798
841
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
799
842
|
const executor = getExecutor(features);
|
|
800
843
|
let artifacts = [];
|
|
801
844
|
let targetsToUpdate = [];
|
|
802
845
|
const firmwareRelease = asRelease(firmware);
|
|
803
|
-
const
|
|
846
|
+
const validatedForceTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
|
|
847
|
+
const forcedTargets = new Set(validatedForceTargets);
|
|
804
848
|
const shouldUpdateFirmware = isUpgrade(firmware) || forcedTargets.has('firmware');
|
|
805
849
|
const shouldUpdateResource = isUpgrade(firmware) || forcedTargets.has('resource');
|
|
850
|
+
if (executor === 'v4' &&
|
|
851
|
+
validatedForceTargets.some(target => target === 'ble' || target === 'bootloader')) {
|
|
852
|
+
planError('Protocol V2 does not support forced BLE or legacy bootloader targets');
|
|
853
|
+
}
|
|
806
854
|
if (executor === 'v4' && (shouldUpdateFirmware || shouldUpdateResource)) {
|
|
807
|
-
const protocolV2 = buildProtocolV2Artifacts(firmwareRelease !== null && firmwareRelease !== void 0 ? firmwareRelease : {}
|
|
855
|
+
const protocolV2 = buildProtocolV2Artifacts(firmwareRelease !== null && firmwareRelease !== void 0 ? firmwareRelease : {}, {
|
|
856
|
+
includeComponents: shouldUpdateFirmware,
|
|
857
|
+
includeResources: shouldUpdateResource,
|
|
858
|
+
});
|
|
808
859
|
artifacts = protocolV2.artifacts;
|
|
809
860
|
targetsToUpdate = protocolV2.targets;
|
|
810
861
|
}
|
|
@@ -862,6 +913,12 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
|
|
|
862
913
|
}
|
|
863
914
|
targetsToUpdate = [...new Set(artifacts.map(artifact => artifact.target))];
|
|
864
915
|
}
|
|
916
|
+
assertForcedTargetsRepresented({
|
|
917
|
+
executor,
|
|
918
|
+
forcedTargets: validatedForceTargets,
|
|
919
|
+
artifacts,
|
|
920
|
+
targetsToUpdate,
|
|
921
|
+
});
|
|
865
922
|
const deviceIdentity = getDeviceUUID(features);
|
|
866
923
|
if (artifacts.length > 0 &&
|
|
867
924
|
(platform === 'native' || platform === 'desktop') &&
|
|
@@ -878,7 +935,7 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
|
|
|
878
935
|
artifacts,
|
|
879
936
|
targetsToUpdate,
|
|
880
937
|
};
|
|
881
|
-
return Object.assign(Object.assign({}, planWithoutDigest), { planDigest: digestFirmwareUpdatePlan(planWithoutDigest) });
|
|
938
|
+
return assertFirmwareUpdatePlan(Object.assign(Object.assign({}, planWithoutDigest), { planDigest: digestFirmwareUpdatePlan(planWithoutDigest) }));
|
|
882
939
|
};
|
|
883
940
|
|
|
884
941
|
const preparedPlanError = (message) => {
|
|
@@ -46172,6 +46229,7 @@ class CheckAllFirmwareRelease extends BaseMethod {
|
|
|
46172
46229
|
return __awaiter(this, void 0, void 0, function* () {
|
|
46173
46230
|
const { features } = this.device;
|
|
46174
46231
|
const { checkBridgeRelease, firmwareType: firmwareTypeParams, platform, forceUpdateTargets, } = this.payload;
|
|
46232
|
+
const validatedForceUpdateTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
|
|
46175
46233
|
if (!features) {
|
|
46176
46234
|
return Promise.resolve(null);
|
|
46177
46235
|
}
|
|
@@ -46205,11 +46263,12 @@ class CheckAllFirmwareRelease extends BaseMethod {
|
|
|
46205
46263
|
firmware: firmwareRelease,
|
|
46206
46264
|
ble: bleFirmwareReleaseInfo,
|
|
46207
46265
|
bootloader: bootloaderRelease,
|
|
46208
|
-
forceUpdateTargets,
|
|
46266
|
+
forceUpdateTargets: validatedForceUpdateTargets,
|
|
46209
46267
|
});
|
|
46210
46268
|
}
|
|
46211
46269
|
catch (error) {
|
|
46212
|
-
if (
|
|
46270
|
+
if (validatedForceUpdateTargets.length > 0 ||
|
|
46271
|
+
!(error instanceof hdShared.HardwareError) ||
|
|
46213
46272
|
((_c = error.params) === null || _c === void 0 ? void 0 : _c.firmwareUpdateCode) !== 'FirmwarePlanInvalid') {
|
|
46214
46273
|
throw error;
|
|
46215
46274
|
}
|
|
@@ -48369,6 +48428,14 @@ class FirmwareUpdate extends BaseMethod {
|
|
|
48369
48428
|
}
|
|
48370
48429
|
}
|
|
48371
48430
|
|
|
48431
|
+
const normalizeFirmwarePreparationError = (error) => {
|
|
48432
|
+
var _a;
|
|
48433
|
+
if (error instanceof hdShared.HardwareError && ((_a = error.params) === null || _a === void 0 ? void 0 : _a.firmwareUpdateCode)) {
|
|
48434
|
+
return error;
|
|
48435
|
+
}
|
|
48436
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, error instanceof Error ? error.message : String(error));
|
|
48437
|
+
};
|
|
48438
|
+
|
|
48372
48439
|
const Log$7 = getLogger(exports.LoggerNames.Method);
|
|
48373
48440
|
const FIRMWARE_DOWNLOAD_REQUEST_OPTIONS = {
|
|
48374
48441
|
connectTimeoutMs: 60000,
|
|
@@ -48651,7 +48718,6 @@ class FirmwareUpdateV2 extends BaseMethod {
|
|
|
48651
48718
|
let preparedSource;
|
|
48652
48719
|
try {
|
|
48653
48720
|
const acquireFirmwareSource = () => __awaiter(this, void 0, void 0, function* () {
|
|
48654
|
-
var _k;
|
|
48655
48721
|
try {
|
|
48656
48722
|
if (preparedSource) {
|
|
48657
48723
|
return preparedSource;
|
|
@@ -48715,7 +48781,7 @@ class FirmwareUpdateV2 extends BaseMethod {
|
|
|
48715
48781
|
return source;
|
|
48716
48782
|
}
|
|
48717
48783
|
catch (err) {
|
|
48718
|
-
throw
|
|
48784
|
+
throw normalizeFirmwarePreparationError(err);
|
|
48719
48785
|
}
|
|
48720
48786
|
});
|
|
48721
48787
|
if (!device.isBootloader() && features) {
|
|
@@ -48916,7 +48982,7 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
|
|
|
48916
48982
|
});
|
|
48917
48983
|
}
|
|
48918
48984
|
runProtocolV1() {
|
|
48919
|
-
var _a, _b
|
|
48985
|
+
var _a, _b;
|
|
48920
48986
|
return __awaiter(this, void 0, void 0, function* () {
|
|
48921
48987
|
const { device } = this;
|
|
48922
48988
|
const { features } = device;
|
|
@@ -48950,7 +49016,7 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
|
|
|
48950
49016
|
}
|
|
48951
49017
|
catch (err) {
|
|
48952
49018
|
yield this.closeArtifactSources();
|
|
48953
|
-
throw
|
|
49019
|
+
throw normalizeFirmwarePreparationError(err);
|
|
48954
49020
|
}
|
|
48955
49021
|
if (!bootloaderSource && fwSources.length === 0) {
|
|
48956
49022
|
yield this.closeArtifactSources();
|
|
@@ -49874,7 +49940,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
49874
49940
|
});
|
|
49875
49941
|
}
|
|
49876
49942
|
runProtocolV2() {
|
|
49877
|
-
var _a, _b
|
|
49943
|
+
var _a, _b;
|
|
49878
49944
|
return __awaiter(this, void 0, void 0, function* () {
|
|
49879
49945
|
yield this.captureProtocolV2PhysicalIdentity();
|
|
49880
49946
|
const deviceFeatures = yield this.getProtocolV2DeviceFeatures();
|
|
@@ -49912,7 +49978,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
49912
49978
|
this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
49913
49979
|
}
|
|
49914
49980
|
catch (err) {
|
|
49915
|
-
throw
|
|
49981
|
+
throw normalizeFirmwarePreparationError(err);
|
|
49916
49982
|
}
|
|
49917
49983
|
if (!bootloaderBinary && fwBinaryMap.length === 0 && !(resourceBundles === null || resourceBundles === void 0 ? void 0 : resourceBundles.length)) {
|
|
49918
49984
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, 'No firmware to update');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-core",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.28",
|
|
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.
|
|
29
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
28
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.28",
|
|
29
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.28",
|
|
30
30
|
"axios": "1.15.2",
|
|
31
31
|
"bignumber.js": "^9.0.2",
|
|
32
32
|
"bytebuffer": "^5.0.1",
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"@types/w3c-web-usb": "^1.0.10",
|
|
45
45
|
"@types/web-bluetooth": "^0.0.21"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "b7c7f9f53c4d745f127d03e0b173e8b759bf8e15"
|
|
48
48
|
}
|
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
getBootloaderReleaseInfo,
|
|
8
8
|
getFirmwareReleaseInfo,
|
|
9
9
|
} from './firmware/releaseHelper';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
buildFirmwareUpdatePlan,
|
|
12
|
+
validateFirmwareUpdatePlanForceTargets,
|
|
13
|
+
} from './firmware/FirmwareUpdatePlan';
|
|
11
14
|
import { getBridgeReleaseInfo } from '../utils/bridgeUpdate';
|
|
12
15
|
import {
|
|
13
16
|
LoggerNames,
|
|
@@ -40,6 +43,7 @@ export default class CheckAllFirmwareRelease extends BaseMethod {
|
|
|
40
43
|
platform,
|
|
41
44
|
forceUpdateTargets,
|
|
42
45
|
} = this.payload as CheckAllFirmwareReleaseParams;
|
|
46
|
+
const validatedForceUpdateTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
|
|
43
47
|
|
|
44
48
|
if (!features) {
|
|
45
49
|
return Promise.resolve(null);
|
|
@@ -79,10 +83,11 @@ export default class CheckAllFirmwareRelease extends BaseMethod {
|
|
|
79
83
|
firmware: firmwareRelease,
|
|
80
84
|
ble: bleFirmwareReleaseInfo,
|
|
81
85
|
bootloader: bootloaderRelease,
|
|
82
|
-
forceUpdateTargets,
|
|
86
|
+
forceUpdateTargets: validatedForceUpdateTargets,
|
|
83
87
|
});
|
|
84
88
|
} catch (error) {
|
|
85
89
|
if (
|
|
90
|
+
validatedForceUpdateTargets.length > 0 ||
|
|
86
91
|
!(error instanceof HardwareError) ||
|
|
87
92
|
error.params?.firmwareUpdateCode !== 'FirmwarePlanInvalid'
|
|
88
93
|
) {
|
|
@@ -14,6 +14,7 @@ import { BaseMethod } from './BaseMethod';
|
|
|
14
14
|
import { validateParams } from './helpers/paramsValidator';
|
|
15
15
|
import { DevicePool } from '../device/DevicePool';
|
|
16
16
|
import { getBinary, getInfo, getSysResourceBinary } from './firmware/getBinary';
|
|
17
|
+
import { normalizeFirmwarePreparationError } from './firmware/FirmwarePreparationError';
|
|
17
18
|
import {
|
|
18
19
|
updateResources,
|
|
19
20
|
updateResourcesFromSources,
|
|
@@ -27,8 +28,8 @@ import { DEVICE } from '../events';
|
|
|
27
28
|
import { type FirmwareByteSource, openFirmwareByteSource } from './firmware/FirmwareArtifactSource';
|
|
28
29
|
import { resolveFirmwareUpdateHostBinding } from './firmware/FirmwareHostBinding';
|
|
29
30
|
import {
|
|
30
|
-
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
31
31
|
assertFirmwareUpdatePreparedPlanBinding,
|
|
32
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
32
33
|
getFirmwareUpdateResourceName,
|
|
33
34
|
} from './firmware/FirmwareUpdatePreparedPlan';
|
|
34
35
|
|
|
@@ -503,10 +504,7 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
|
|
|
503
504
|
this.postTipMessage('DownloadFirmwareSuccess');
|
|
504
505
|
return source;
|
|
505
506
|
} catch (err) {
|
|
506
|
-
throw
|
|
507
|
-
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
508
|
-
err.message ?? err
|
|
509
|
-
);
|
|
507
|
+
throw normalizeFirmwarePreparationError(err);
|
|
510
508
|
}
|
|
511
509
|
};
|
|
512
510
|
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
getLogger,
|
|
14
14
|
} from '../utils';
|
|
15
15
|
import { getBinary, getSysResourceBinary } from './firmware/getBinary';
|
|
16
|
+
import { normalizeFirmwarePreparationError } from './firmware/FirmwarePreparationError';
|
|
16
17
|
import { DataManager } from '../data-manager';
|
|
17
18
|
import { FirmwareUpdateBaseMethod } from './firmware/FirmwareUpdateBaseMethod';
|
|
18
19
|
import { DevicePool } from '../device/DevicePool';
|
|
@@ -21,8 +22,8 @@ import { buildProtocolV1FeaturesPayload } from '../deviceProfile';
|
|
|
21
22
|
import { openFirmwareByteSource } from './firmware/FirmwareArtifactSource';
|
|
22
23
|
import { resolveFirmwareUpdateHostBinding } from './firmware/FirmwareHostBinding';
|
|
23
24
|
import {
|
|
24
|
-
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
25
25
|
assertFirmwareUpdatePreparedPlanBinding,
|
|
26
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
26
27
|
getFirmwareUpdateResourceName,
|
|
27
28
|
} from './firmware/FirmwareUpdatePreparedPlan';
|
|
28
29
|
|
|
@@ -184,7 +185,7 @@ export default class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
184
185
|
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
185
186
|
} catch (err) {
|
|
186
187
|
await this.closeArtifactSources();
|
|
187
|
-
throw
|
|
188
|
+
throw normalizeFirmwarePreparationError(err);
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
if (!bootloaderSource && fwSources.length === 0) {
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
getLogger,
|
|
19
19
|
} from '../utils';
|
|
20
20
|
import { getSysResourceBinary } from './firmware/getBinary';
|
|
21
|
+
import { normalizeFirmwarePreparationError } from './firmware/FirmwarePreparationError';
|
|
21
22
|
import { DataManager } from '../data-manager';
|
|
22
23
|
import { FirmwareUpdateBaseMethod } from './firmware/FirmwareUpdateBaseMethod';
|
|
23
24
|
import { DevicePool } from '../device/DevicePool';
|
|
@@ -625,7 +626,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
625
626
|
: undefined;
|
|
626
627
|
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
627
628
|
} catch (err) {
|
|
628
|
-
throw
|
|
629
|
+
throw normalizeFirmwarePreparationError(err);
|
|
629
630
|
}
|
|
630
631
|
|
|
631
632
|
if (!bootloaderBinary && fwBinaryMap.length === 0 && !resourceBundles?.length) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ERRORS, HardwareError, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
export const normalizeFirmwarePreparationError = (error: unknown) => {
|
|
4
|
+
if (error instanceof HardwareError && error.params?.firmwareUpdateCode) {
|
|
5
|
+
return error;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return ERRORS.TypedError(
|
|
9
|
+
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
10
|
+
error instanceof Error ? error.message : String(error)
|
|
11
|
+
);
|
|
12
|
+
};
|
|
@@ -166,6 +166,30 @@ const planError = (message: string): never => {
|
|
|
166
166
|
});
|
|
167
167
|
};
|
|
168
168
|
|
|
169
|
+
const FIRMWARE_UPDATE_PLAN_FORCE_TARGETS = new Set<FirmwareUpdatePlanForceTarget>([
|
|
170
|
+
'firmware',
|
|
171
|
+
'ble',
|
|
172
|
+
'bootloader',
|
|
173
|
+
'resource',
|
|
174
|
+
]);
|
|
175
|
+
|
|
176
|
+
export const validateFirmwareUpdatePlanForceTargets = (
|
|
177
|
+
value: unknown
|
|
178
|
+
): FirmwareUpdatePlanForceTarget[] => {
|
|
179
|
+
if (value === undefined) {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
if (
|
|
183
|
+
!Array.isArray(value) ||
|
|
184
|
+
value.length > FIRMWARE_UPDATE_PLAN_FORCE_TARGETS.size ||
|
|
185
|
+
value.some(target => !FIRMWARE_UPDATE_PLAN_FORCE_TARGETS.has(target)) ||
|
|
186
|
+
new Set(value).size !== value.length
|
|
187
|
+
) {
|
|
188
|
+
return planError('Firmware update force targets are invalid');
|
|
189
|
+
}
|
|
190
|
+
return [...value] as FirmwareUpdatePlanForceTarget[];
|
|
191
|
+
};
|
|
192
|
+
|
|
169
193
|
const assertExactKeys = (
|
|
170
194
|
value: Record<string, unknown>,
|
|
171
195
|
required: readonly string[],
|
|
@@ -388,13 +412,20 @@ const getExecutor = (features: Features): FirmwareUpdatePlan['executor'] => {
|
|
|
388
412
|
};
|
|
389
413
|
|
|
390
414
|
const buildProtocolV2Artifacts = (
|
|
391
|
-
release: ReleaseRecord
|
|
415
|
+
release: ReleaseRecord,
|
|
416
|
+
{
|
|
417
|
+
includeComponents = true,
|
|
418
|
+
includeResources = true,
|
|
419
|
+
}: {
|
|
420
|
+
includeComponents?: boolean;
|
|
421
|
+
includeResources?: boolean;
|
|
422
|
+
} = {}
|
|
392
423
|
): {
|
|
393
424
|
artifacts: FirmwareUpdatePlanArtifact[];
|
|
394
425
|
targets: FirmwareUpdatePlanTarget[];
|
|
395
426
|
} => {
|
|
396
427
|
const components = asRecord(release.components);
|
|
397
|
-
if (!components) {
|
|
428
|
+
if (includeComponents && !components) {
|
|
398
429
|
throw ERRORS.TypedError(
|
|
399
430
|
HardwareErrorCode.RuntimeError,
|
|
400
431
|
'Protocol V2 release has no component set',
|
|
@@ -404,15 +435,15 @@ const buildProtocolV2Artifacts = (
|
|
|
404
435
|
const installOrder = Array.isArray(release.installOrder)
|
|
405
436
|
? release.installOrder.filter((key): key is string => typeof key === 'string')
|
|
406
437
|
: [];
|
|
407
|
-
const componentKeys =
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
438
|
+
const componentKeys =
|
|
439
|
+
includeComponents && components
|
|
440
|
+
? [...installOrder, ...Object.keys(components).filter(key => !installOrder.includes(key))]
|
|
441
|
+
: [];
|
|
411
442
|
const artifacts: FirmwareUpdatePlanArtifact[] = [];
|
|
412
443
|
const targets: FirmwareUpdatePlanTarget[] = [];
|
|
413
444
|
const componentTargets = new Set<FirmwareUpdatePlanTarget>();
|
|
414
445
|
for (const key of componentKeys) {
|
|
415
|
-
const component = asRecord(components[key]);
|
|
446
|
+
const component = asRecord(components?.[key]);
|
|
416
447
|
const targetName = asString(component?.target)?.toUpperCase();
|
|
417
448
|
if (targetName === 'ROMLOADER') {
|
|
418
449
|
throw ERRORS.TypedError(
|
|
@@ -453,7 +484,8 @@ const buildProtocolV2Artifacts = (
|
|
|
453
484
|
targets.push(target);
|
|
454
485
|
}
|
|
455
486
|
|
|
456
|
-
const bundles =
|
|
487
|
+
const bundles =
|
|
488
|
+
includeResources && Array.isArray(release.resourceBundles) ? release.resourceBundles : [];
|
|
457
489
|
const resourceBundleNames = new Set<string>();
|
|
458
490
|
for (const value of bundles) {
|
|
459
491
|
const bundle = asRecord(value);
|
|
@@ -492,6 +524,63 @@ const buildProtocolV2Artifacts = (
|
|
|
492
524
|
return { artifacts, targets: [...new Set(targets)] };
|
|
493
525
|
};
|
|
494
526
|
|
|
527
|
+
const isForcedTargetRepresented = ({
|
|
528
|
+
executor,
|
|
529
|
+
forcedTarget,
|
|
530
|
+
artifacts,
|
|
531
|
+
targetsToUpdate,
|
|
532
|
+
}: {
|
|
533
|
+
executor: FirmwareUpdatePlan['executor'];
|
|
534
|
+
forcedTarget: FirmwareUpdatePlanForceTarget;
|
|
535
|
+
artifacts: readonly FirmwareUpdatePlanArtifact[];
|
|
536
|
+
targetsToUpdate: readonly FirmwareUpdatePlanTarget[];
|
|
537
|
+
}): boolean => {
|
|
538
|
+
if (executor !== 'v4') {
|
|
539
|
+
return artifacts.some(
|
|
540
|
+
artifact => artifact.target === forcedTarget && targetsToUpdate.includes(forcedTarget)
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
if (forcedTarget === 'firmware') {
|
|
544
|
+
return artifacts.some(
|
|
545
|
+
artifact => artifact.role === 'component' && targetsToUpdate.includes(artifact.target)
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
if (forcedTarget === 'resource') {
|
|
549
|
+
return artifacts.some(
|
|
550
|
+
artifact =>
|
|
551
|
+
artifact.role === 'resourceBundle' &&
|
|
552
|
+
artifact.target === 'resource' &&
|
|
553
|
+
targetsToUpdate.includes('resource')
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
return false;
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
const assertForcedTargetsRepresented = ({
|
|
560
|
+
executor,
|
|
561
|
+
forcedTargets,
|
|
562
|
+
artifacts,
|
|
563
|
+
targetsToUpdate,
|
|
564
|
+
}: {
|
|
565
|
+
executor: FirmwareUpdatePlan['executor'];
|
|
566
|
+
forcedTargets: readonly FirmwareUpdatePlanForceTarget[];
|
|
567
|
+
artifacts: readonly FirmwareUpdatePlanArtifact[];
|
|
568
|
+
targetsToUpdate: readonly FirmwareUpdatePlanTarget[];
|
|
569
|
+
}) => {
|
|
570
|
+
for (const forcedTarget of forcedTargets) {
|
|
571
|
+
if (
|
|
572
|
+
!isForcedTargetRepresented({
|
|
573
|
+
executor,
|
|
574
|
+
forcedTarget,
|
|
575
|
+
artifacts,
|
|
576
|
+
targetsToUpdate,
|
|
577
|
+
})
|
|
578
|
+
) {
|
|
579
|
+
planError(`Forced firmware update target ${forcedTarget} is not represented by the plan`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
|
|
495
584
|
export const buildFirmwareUpdatePlan = ({
|
|
496
585
|
features,
|
|
497
586
|
firmwareType,
|
|
@@ -513,12 +602,23 @@ export const buildFirmwareUpdatePlan = ({
|
|
|
513
602
|
let artifacts: FirmwareUpdatePlanArtifact[] = [];
|
|
514
603
|
let targetsToUpdate: FirmwareUpdatePlanTarget[] = [];
|
|
515
604
|
const firmwareRelease = asRelease(firmware);
|
|
516
|
-
const
|
|
605
|
+
const validatedForceTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
|
|
606
|
+
const forcedTargets = new Set(validatedForceTargets);
|
|
517
607
|
const shouldUpdateFirmware = isUpgrade(firmware) || forcedTargets.has('firmware');
|
|
518
608
|
const shouldUpdateResource = isUpgrade(firmware) || forcedTargets.has('resource');
|
|
519
609
|
|
|
610
|
+
if (
|
|
611
|
+
executor === 'v4' &&
|
|
612
|
+
validatedForceTargets.some(target => target === 'ble' || target === 'bootloader')
|
|
613
|
+
) {
|
|
614
|
+
planError('Protocol V2 does not support forced BLE or legacy bootloader targets');
|
|
615
|
+
}
|
|
616
|
+
|
|
520
617
|
if (executor === 'v4' && (shouldUpdateFirmware || shouldUpdateResource)) {
|
|
521
|
-
const protocolV2 = buildProtocolV2Artifacts(firmwareRelease ?? {}
|
|
618
|
+
const protocolV2 = buildProtocolV2Artifacts(firmwareRelease ?? {}, {
|
|
619
|
+
includeComponents: shouldUpdateFirmware,
|
|
620
|
+
includeResources: shouldUpdateResource,
|
|
621
|
+
});
|
|
522
622
|
artifacts = protocolV2.artifacts;
|
|
523
623
|
targetsToUpdate = protocolV2.targets;
|
|
524
624
|
} else {
|
|
@@ -609,6 +709,13 @@ export const buildFirmwareUpdatePlan = ({
|
|
|
609
709
|
targetsToUpdate = [...new Set(artifacts.map(artifact => artifact.target))];
|
|
610
710
|
}
|
|
611
711
|
|
|
712
|
+
assertForcedTargetsRepresented({
|
|
713
|
+
executor,
|
|
714
|
+
forcedTargets: validatedForceTargets,
|
|
715
|
+
artifacts,
|
|
716
|
+
targetsToUpdate,
|
|
717
|
+
});
|
|
718
|
+
|
|
612
719
|
const deviceIdentity = getDeviceUUID(features);
|
|
613
720
|
if (
|
|
614
721
|
artifacts.length > 0 &&
|
|
@@ -631,8 +738,8 @@ export const buildFirmwareUpdatePlan = ({
|
|
|
631
738
|
artifacts,
|
|
632
739
|
targetsToUpdate,
|
|
633
740
|
};
|
|
634
|
-
return {
|
|
741
|
+
return assertFirmwareUpdatePlan({
|
|
635
742
|
...planWithoutDigest,
|
|
636
743
|
planDigest: digestFirmwareUpdatePlan(planWithoutDigest),
|
|
637
|
-
};
|
|
744
|
+
});
|
|
638
745
|
};
|