@onekeyfe/hd-core 1.2.0-alpha.82 → 1.2.0-alpha.85
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__/check-all-firmware-release-protocol-v2.test.ts +285 -13
- package/__tests__/check-firmware-release-protocol-v2.test.ts +6 -0
- package/__tests__/firmware-memory-host.test.ts +105 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +15 -70
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +53 -1
- package/__tests__/protocol-v2-resources.test.ts +0 -38
- package/__tests__/protocol-v2.test.ts +212 -320
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +1 -2
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareMemoryHost.d.ts +27 -0
- package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +33 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +0 -1
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/protocolV2Release.d.ts.map +1 -1
- package/dist/index.d.ts +25 -64
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +546 -387
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +0 -17
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -0
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +0 -1
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +0 -10
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +0 -2
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/settings.d.ts +0 -11
- package/dist/types/settings.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +72 -5
- package/src/api/FirmwareUpdateV4.ts +154 -165
- package/src/api/firmware/FirmwareMemoryHost.ts +171 -0
- package/src/api/firmware/FirmwareUpdatePlan.ts +176 -79
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +8 -1
- package/src/api/firmware/protocolV2Release.ts +2 -0
- package/src/index.ts +6 -1
- package/src/inject.ts +0 -2
- package/src/protocols/protocol-v2/resources.ts +0 -47
- package/src/types/api/checkAllFirmwareRelease.ts +1 -0
- package/src/types/api/export.ts +0 -4
- package/src/types/api/firmwareUpdate.ts +0 -15
- package/src/types/api/index.ts +0 -2
- package/src/types/settings.ts +0 -20
- package/src/utils/deviceFeaturesUtils.ts +2 -2
- package/dist/types/api/protocolV2ResourceManifest.d.ts +0 -17
- package/dist/types/api/protocolV2ResourceManifest.d.ts.map +0 -1
- package/src/types/api/protocolV2ResourceManifest.ts +0 -19
|
@@ -62,14 +62,6 @@ const release: IFirmwareReleaseInfo = {
|
|
|
62
62
|
version: [2, 0, 0],
|
|
63
63
|
},
|
|
64
64
|
},
|
|
65
|
-
resourceBundles: [
|
|
66
|
-
{
|
|
67
|
-
name: 'images',
|
|
68
|
-
url: 'https://example.com/images.okpkg',
|
|
69
|
-
devicePath: 'vol0:/bundles/images/images.okpkg',
|
|
70
|
-
version: [1, 0, 0],
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
65
|
};
|
|
74
66
|
|
|
75
67
|
const resourceSource = {
|
|
@@ -152,7 +144,6 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
152
144
|
test('detects same-version package hotfixes by P1 and P2 payload hash', () => {
|
|
153
145
|
const packageSet: IFirmwareReleaseInfo = {
|
|
154
146
|
...release,
|
|
155
|
-
resourceBundles: undefined,
|
|
156
147
|
installOrder: ['applicationP1', 'applicationP2'],
|
|
157
148
|
components: {
|
|
158
149
|
applicationP1: {
|
|
@@ -203,7 +194,6 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
203
194
|
const packageSet: IFirmwareReleaseInfo = {
|
|
204
195
|
...release,
|
|
205
196
|
required: false,
|
|
206
|
-
resourceBundles: undefined,
|
|
207
197
|
installOrder: ['applicationP1'],
|
|
208
198
|
components: {
|
|
209
199
|
applicationP1: {
|
|
@@ -234,7 +224,6 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
234
224
|
const packageSet: IFirmwareReleaseInfo = {
|
|
235
225
|
...release,
|
|
236
226
|
required: false,
|
|
237
|
-
resourceBundles: undefined,
|
|
238
227
|
installOrder: ['applicationP1'],
|
|
239
228
|
components: {
|
|
240
229
|
applicationP1: {
|
|
@@ -265,7 +254,6 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
265
254
|
test('updates both application packages when normal mode only reports P1', () => {
|
|
266
255
|
const packageSet: IFirmwareReleaseInfo = {
|
|
267
256
|
...release,
|
|
268
|
-
resourceBundles: undefined,
|
|
269
257
|
installOrder: ['applicationP1', 'applicationP2'],
|
|
270
258
|
components: {
|
|
271
259
|
applicationP1: {
|
|
@@ -333,6 +321,21 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
333
321
|
resourceArchive: resourceSource,
|
|
334
322
|
resourcePreparationRequired: true,
|
|
335
323
|
targetsToUpdate: ['boot', 'app_v1', 'resource'],
|
|
324
|
+
firmwareUpdatePlan: {
|
|
325
|
+
executor: 'v4',
|
|
326
|
+
platform: 'web',
|
|
327
|
+
artifacts: [
|
|
328
|
+
{ artifactId: 'component:boot', target: 'boot' },
|
|
329
|
+
{ artifactId: 'component:app_v1', target: 'app_v1' },
|
|
330
|
+
{
|
|
331
|
+
artifactId: 'resource:archive',
|
|
332
|
+
target: 'resource',
|
|
333
|
+
role: 'resourceBundle',
|
|
334
|
+
container: 'zip',
|
|
335
|
+
},
|
|
336
|
+
],
|
|
337
|
+
targetsToUpdate: ['boot', 'app_v1', 'resource'],
|
|
338
|
+
},
|
|
336
339
|
firmware: {
|
|
337
340
|
status: 'required',
|
|
338
341
|
},
|
|
@@ -344,11 +347,266 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
344
347
|
expect(method.getSupportedProtocols()).toEqual(['V1', 'V2']);
|
|
345
348
|
});
|
|
346
349
|
|
|
350
|
+
test('preserves platform and forced firmware targets in the Protocol V2 prepared plan', async () => {
|
|
351
|
+
const method = new CheckAllFirmwareRelease({
|
|
352
|
+
id: 1,
|
|
353
|
+
payload: {
|
|
354
|
+
method: 'checkAllFirmwareRelease',
|
|
355
|
+
firmwareType: EFirmwareType.Universal,
|
|
356
|
+
platform: 'native',
|
|
357
|
+
forceUpdateTargets: ['firmware'],
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
method.init();
|
|
361
|
+
method.device = {
|
|
362
|
+
isProtocolV2: () => true,
|
|
363
|
+
features: {
|
|
364
|
+
deviceType: 'pro2',
|
|
365
|
+
serialNo: 'pro2-device-id',
|
|
366
|
+
firmwareVersion: '1.0.0',
|
|
367
|
+
},
|
|
368
|
+
getDeviceState: jest.fn().mockResolvedValue({
|
|
369
|
+
identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
|
|
370
|
+
status: { mode: 'normal' },
|
|
371
|
+
versions: currentVersions,
|
|
372
|
+
}),
|
|
373
|
+
} as unknown as CheckAllFirmwareRelease['device'];
|
|
374
|
+
jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
|
|
375
|
+
jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
|
|
376
|
+
|
|
377
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
378
|
+
targetsToUpdate: ['boot', 'app_v1', 'se02', 'se03', 'resource'],
|
|
379
|
+
firmwareUpdatePlan: {
|
|
380
|
+
executor: 'v4',
|
|
381
|
+
deviceIdentity: 'pro2-device-id',
|
|
382
|
+
platform: 'native',
|
|
383
|
+
artifacts: [
|
|
384
|
+
{ artifactId: 'component:boot', target: 'boot' },
|
|
385
|
+
{ artifactId: 'component:app_v1', target: 'app_v1' },
|
|
386
|
+
{ artifactId: 'component:se02', target: 'se02' },
|
|
387
|
+
{ artifactId: 'component:se03', target: 'se03' },
|
|
388
|
+
{ artifactId: 'resource:archive', target: 'resource', container: 'zip' },
|
|
389
|
+
],
|
|
390
|
+
targetsToUpdate: ['boot', 'app_v1', 'se02', 'se03', 'resource'],
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test('includes exact Protocol V2 developer force targets in the prepared plan', async () => {
|
|
396
|
+
const currentRelease: IFirmwareReleaseInfo = {
|
|
397
|
+
...release,
|
|
398
|
+
required: false,
|
|
399
|
+
version: [1, 0, 0],
|
|
400
|
+
installOrder: ['applicationP1', 'coprocessor'],
|
|
401
|
+
components: {
|
|
402
|
+
applicationP1: {
|
|
403
|
+
target: 'APPLICATION_P1',
|
|
404
|
+
url: 'https://example.com/application-p1.okpkg',
|
|
405
|
+
version: [1, 0, 0],
|
|
406
|
+
},
|
|
407
|
+
coprocessor: {
|
|
408
|
+
target: 'COPROCESSOR',
|
|
409
|
+
url: 'https://example.com/coprocessor.okpkg',
|
|
410
|
+
version: [1, 0, 0],
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
};
|
|
414
|
+
const method = new CheckAllFirmwareRelease({
|
|
415
|
+
id: 1,
|
|
416
|
+
payload: {
|
|
417
|
+
method: 'checkAllFirmwareRelease',
|
|
418
|
+
platform: 'desktop',
|
|
419
|
+
protocolV2ForceUpdateTargets: ['app_v1', 'coprocessor'],
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
method.init();
|
|
423
|
+
method.device = {
|
|
424
|
+
isProtocolV2: () => true,
|
|
425
|
+
features: {
|
|
426
|
+
deviceType: 'pro2',
|
|
427
|
+
serialNo: 'pro2-device-id',
|
|
428
|
+
firmwareVersion: '1.0.0',
|
|
429
|
+
},
|
|
430
|
+
getDeviceState: jest.fn().mockResolvedValue({
|
|
431
|
+
identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
|
|
432
|
+
status: { mode: 'normal' },
|
|
433
|
+
versions: { ...currentVersions, applicationP1: '1.0.0' },
|
|
434
|
+
}),
|
|
435
|
+
} as unknown as CheckAllFirmwareRelease['device'];
|
|
436
|
+
jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(currentRelease);
|
|
437
|
+
jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
|
|
438
|
+
|
|
439
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
440
|
+
targetsToUpdate: ['app_v1', 'coprocessor', 'resource'],
|
|
441
|
+
firmwareUpdatePlan: {
|
|
442
|
+
executor: 'v4',
|
|
443
|
+
platform: 'desktop',
|
|
444
|
+
artifacts: [
|
|
445
|
+
{ artifactId: 'component:app_v1', target: 'app_v1' },
|
|
446
|
+
{ artifactId: 'component:coprocessor', target: 'coprocessor' },
|
|
447
|
+
{ artifactId: 'resource:archive', target: 'resource', container: 'zip' },
|
|
448
|
+
],
|
|
449
|
+
targetsToUpdate: ['app_v1', 'coprocessor', 'resource'],
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
test('keeps exact force targets but omits a Desktop Plan without device identity', async () => {
|
|
455
|
+
const currentRelease: IFirmwareReleaseInfo = {
|
|
456
|
+
...release,
|
|
457
|
+
required: false,
|
|
458
|
+
version: [1, 0, 0],
|
|
459
|
+
installOrder: ['applicationP1'],
|
|
460
|
+
components: {
|
|
461
|
+
applicationP1: {
|
|
462
|
+
target: 'APPLICATION_P1',
|
|
463
|
+
url: 'https://example.com/application-p1.okpkg',
|
|
464
|
+
version: [1, 0, 0],
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
const method = new CheckAllFirmwareRelease({
|
|
469
|
+
id: 1,
|
|
470
|
+
payload: {
|
|
471
|
+
method: 'checkAllFirmwareRelease',
|
|
472
|
+
platform: 'desktop',
|
|
473
|
+
protocolV2ForceUpdateTargets: ['app_v1'],
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
method.init();
|
|
477
|
+
method.device = {
|
|
478
|
+
isProtocolV2: () => true,
|
|
479
|
+
features: { deviceType: 'pro2', firmwareVersion: '1.0.0' },
|
|
480
|
+
getDeviceState: jest.fn().mockResolvedValue({
|
|
481
|
+
identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
|
|
482
|
+
status: { mode: 'normal' },
|
|
483
|
+
versions: { ...currentVersions, applicationP1: '1.0.0' },
|
|
484
|
+
}),
|
|
485
|
+
} as unknown as CheckAllFirmwareRelease['device'];
|
|
486
|
+
jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(currentRelease);
|
|
487
|
+
jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
|
|
488
|
+
|
|
489
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
490
|
+
targetsToUpdate: ['app_v1', 'resource'],
|
|
491
|
+
firmwareUpdatePlan: undefined,
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
test('rejects Neo-only unsupported exact secure-element targets', async () => {
|
|
496
|
+
const neoRelease: IFirmwareReleaseInfo = {
|
|
497
|
+
...release,
|
|
498
|
+
required: false,
|
|
499
|
+
installOrder: ['se03'],
|
|
500
|
+
components: {
|
|
501
|
+
se03: {
|
|
502
|
+
target: 'SE03',
|
|
503
|
+
url: 'https://example.com/se03.okpkg',
|
|
504
|
+
version: [1, 0, 0],
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
const method = new CheckAllFirmwareRelease({
|
|
509
|
+
id: 1,
|
|
510
|
+
payload: {
|
|
511
|
+
method: 'checkAllFirmwareRelease',
|
|
512
|
+
protocolV2ForceUpdateTargets: ['se03'],
|
|
513
|
+
},
|
|
514
|
+
});
|
|
515
|
+
method.init();
|
|
516
|
+
method.device = {
|
|
517
|
+
isProtocolV2: () => true,
|
|
518
|
+
features: { deviceType: 'neo', firmwareVersion: '1.0.0' },
|
|
519
|
+
getDeviceState: jest.fn().mockResolvedValue({
|
|
520
|
+
identity: { deviceType: 'neo', firmwareType: EFirmwareType.Universal },
|
|
521
|
+
status: { mode: 'normal' },
|
|
522
|
+
versions: currentVersions,
|
|
523
|
+
}),
|
|
524
|
+
} as unknown as CheckAllFirmwareRelease['device'];
|
|
525
|
+
jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(neoRelease);
|
|
526
|
+
jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
|
|
527
|
+
|
|
528
|
+
await expect(method.run()).rejects.toMatchObject({
|
|
529
|
+
params: { firmwareUpdateCode: 'FirmwarePlanInvalid' },
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
test('builds a V4 plan for Neo and excludes unsupported secure elements when forced', async () => {
|
|
534
|
+
const neoRelease: IFirmwareReleaseInfo = {
|
|
535
|
+
...release,
|
|
536
|
+
installOrder: ['se01', 'se02', 'se03', 'se04'],
|
|
537
|
+
components: {
|
|
538
|
+
se01: { target: 'SE01', url: 'https://example.com/se01.bin', version: [1, 0, 0] },
|
|
539
|
+
se02: { target: 'SE02', url: 'https://example.com/se02.bin', version: [2, 0, 0] },
|
|
540
|
+
se03: { target: 'SE03', url: 'https://example.com/se03.bin', version: [1, 0, 0] },
|
|
541
|
+
se04: { target: 'SE04', url: 'https://example.com/se04.bin', version: [1, 0, 0] },
|
|
542
|
+
},
|
|
543
|
+
};
|
|
544
|
+
const method = new CheckAllFirmwareRelease({
|
|
545
|
+
id: 1,
|
|
546
|
+
payload: {
|
|
547
|
+
method: 'checkAllFirmwareRelease',
|
|
548
|
+
platform: 'native',
|
|
549
|
+
forceUpdateTargets: ['firmware'],
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
method.init();
|
|
553
|
+
method.device = {
|
|
554
|
+
isProtocolV2: () => true,
|
|
555
|
+
features: {
|
|
556
|
+
deviceType: 'neo',
|
|
557
|
+
serialNo: 'neo-device-id',
|
|
558
|
+
firmwareVersion: '1.0.0',
|
|
559
|
+
},
|
|
560
|
+
getDeviceState: jest.fn().mockResolvedValue({
|
|
561
|
+
identity: { deviceType: 'neo', firmwareType: EFirmwareType.Universal },
|
|
562
|
+
status: { mode: 'normal' },
|
|
563
|
+
versions: currentVersions,
|
|
564
|
+
}),
|
|
565
|
+
} as unknown as CheckAllFirmwareRelease['device'];
|
|
566
|
+
jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(neoRelease);
|
|
567
|
+
jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
|
|
568
|
+
|
|
569
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
570
|
+
targetsToUpdate: ['se01', 'se02', 'resource'],
|
|
571
|
+
firmwareUpdatePlan: {
|
|
572
|
+
executor: 'v4',
|
|
573
|
+
deviceModel: 'neo',
|
|
574
|
+
platform: 'native',
|
|
575
|
+
targetsToUpdate: ['se01', 'se02', 'resource'],
|
|
576
|
+
},
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
test('rejects a forced Protocol V2 resource target without a configured archive', async () => {
|
|
581
|
+
const method = new CheckAllFirmwareRelease({
|
|
582
|
+
id: 1,
|
|
583
|
+
payload: {
|
|
584
|
+
method: 'checkAllFirmwareRelease',
|
|
585
|
+
forceUpdateTargets: ['resource'],
|
|
586
|
+
},
|
|
587
|
+
});
|
|
588
|
+
method.init();
|
|
589
|
+
method.device = {
|
|
590
|
+
isProtocolV2: () => true,
|
|
591
|
+
features: { deviceType: 'pro2', firmwareVersion: '1.0.0' },
|
|
592
|
+
getDeviceState: jest.fn().mockResolvedValue({
|
|
593
|
+
identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
|
|
594
|
+
status: { mode: 'normal' },
|
|
595
|
+
versions: currentVersions,
|
|
596
|
+
}),
|
|
597
|
+
} as unknown as CheckAllFirmwareRelease['device'];
|
|
598
|
+
jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
|
|
599
|
+
jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(undefined);
|
|
600
|
+
|
|
601
|
+
await expect(method.run()).rejects.toMatchObject({
|
|
602
|
+
params: { firmwareUpdateCode: 'FirmwarePlanInvalid' },
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
|
|
347
606
|
test('requests and compares firmware hashes only when explicitly enabled', async () => {
|
|
348
607
|
const packageSet: IFirmwareReleaseInfo = {
|
|
349
608
|
...release,
|
|
350
609
|
required: false,
|
|
351
|
-
resourceBundles: undefined,
|
|
352
610
|
installOrder: ['applicationP1'],
|
|
353
611
|
components: {
|
|
354
612
|
applicationP1: {
|
|
@@ -467,6 +725,18 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
467
725
|
resourceStatus: 'unknown',
|
|
468
726
|
resourcePreparationRequired: true,
|
|
469
727
|
targetsToUpdate: ['resource'],
|
|
728
|
+
firmwareUpdatePlan: {
|
|
729
|
+
executor: 'v4',
|
|
730
|
+
artifacts: [
|
|
731
|
+
{
|
|
732
|
+
artifactId: 'resource:archive',
|
|
733
|
+
target: 'resource',
|
|
734
|
+
role: 'resourceBundle',
|
|
735
|
+
container: 'zip',
|
|
736
|
+
},
|
|
737
|
+
],
|
|
738
|
+
targetsToUpdate: ['resource'],
|
|
739
|
+
},
|
|
470
740
|
});
|
|
471
741
|
});
|
|
472
742
|
|
|
@@ -477,6 +747,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
477
747
|
await api.checkAllFirmwareRelease('pro2-connect-id', {
|
|
478
748
|
checkFirmwareHash: true,
|
|
479
749
|
firmwareType: EFirmwareType.Universal,
|
|
750
|
+
protocolV2ForceUpdateTargets: ['app_v1'],
|
|
480
751
|
retryCount: 0,
|
|
481
752
|
});
|
|
482
753
|
|
|
@@ -484,6 +755,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
484
755
|
connectId: 'pro2-connect-id',
|
|
485
756
|
checkFirmwareHash: true,
|
|
486
757
|
firmwareType: EFirmwareType.Universal,
|
|
758
|
+
protocolV2ForceUpdateTargets: ['app_v1'],
|
|
487
759
|
retryCount: 0,
|
|
488
760
|
method: 'checkAllFirmwareRelease',
|
|
489
761
|
});
|
|
@@ -22,6 +22,10 @@ const release: IFirmwareReleaseInfo = {
|
|
|
22
22
|
'zh-CN': '更新',
|
|
23
23
|
'en-US': 'Update',
|
|
24
24
|
},
|
|
25
|
+
bootloaderChangelog: {
|
|
26
|
+
'zh-CN': '旧版 Bootloader 更新',
|
|
27
|
+
'en-US': 'Legacy bootloader update',
|
|
28
|
+
},
|
|
25
29
|
installOrder: ['bootloader', 'applicationP1', 'coprocessor'],
|
|
26
30
|
components: {
|
|
27
31
|
bootloader: {
|
|
@@ -127,6 +131,7 @@ describe.each(['pro2', 'neo'] as const)('Protocol V2 release checks for %s', dev
|
|
|
127
131
|
await expect(bootloaderMethod.run()).resolves.toMatchObject({
|
|
128
132
|
status: 'outdated',
|
|
129
133
|
shouldUpdate: true,
|
|
134
|
+
changelog: [release.changelog],
|
|
130
135
|
release: {
|
|
131
136
|
protocol: 'V2',
|
|
132
137
|
configKey: 'bootloader',
|
|
@@ -134,6 +139,7 @@ describe.each(['pro2', 'neo'] as const)('Protocol V2 release checks for %s', dev
|
|
|
134
139
|
target: 'BOOTLOADER',
|
|
135
140
|
url: 'https://example.com/bootloader.okpkg',
|
|
136
141
|
version: [2, 0, 0],
|
|
142
|
+
changelog: release.changelog,
|
|
137
143
|
},
|
|
138
144
|
});
|
|
139
145
|
await expect(allMethod.run()).resolves.toMatchObject({
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { prepareFirmwareUpdateV4MemoryHost } from '../src/api/firmware/FirmwareMemoryHost';
|
|
2
|
+
|
|
3
|
+
import type { CoreApi } from '../src/types/api';
|
|
4
|
+
import type { FirmwareUpdatePlan } from '../src/types/api/firmwareUpdatePlan';
|
|
5
|
+
|
|
6
|
+
describe('prepareFirmwareUpdateV4MemoryHost', () => {
|
|
7
|
+
test('registers artifact-backed component and resource readers', async () => {
|
|
8
|
+
const componentBinary = new Uint8Array([1, 2, 3, 4]).buffer;
|
|
9
|
+
const archiveBinary = new Uint8Array([5, 6]).buffer;
|
|
10
|
+
const manifestBinary = new TextEncoder().encode('{"schema":1}').buffer;
|
|
11
|
+
let artifactReader: Parameters<
|
|
12
|
+
CoreApi['registerFirmwareUpdateHostBinding']
|
|
13
|
+
>[0]['artifactReader'];
|
|
14
|
+
const unregisterFirmwareUpdateHostBinding = jest.fn();
|
|
15
|
+
const sdk = {
|
|
16
|
+
prepareFirmwareUpdatePlan: jest.fn(({ plan, artifacts }) => ({
|
|
17
|
+
schemaVersion: 2,
|
|
18
|
+
preparedPlanDigest: 'b'.repeat(64),
|
|
19
|
+
planDigest: plan.planDigest,
|
|
20
|
+
networkPolicy: 'forbid',
|
|
21
|
+
executor: plan.executor,
|
|
22
|
+
deviceIdentity: plan.deviceIdentity,
|
|
23
|
+
deviceModel: plan.deviceModel,
|
|
24
|
+
firmwareType: plan.firmwareType,
|
|
25
|
+
platform: plan.platform,
|
|
26
|
+
leaseRef: 'test-lease',
|
|
27
|
+
targetsToUpdate: plan.targetsToUpdate,
|
|
28
|
+
artifacts: plan.artifacts.map(planArtifact => {
|
|
29
|
+
const input = artifacts.find(item => item.artifactId === planArtifact.artifactId);
|
|
30
|
+
return {
|
|
31
|
+
...planArtifact,
|
|
32
|
+
artifact: input?.artifact,
|
|
33
|
+
materializedEntries: input?.materializedEntries,
|
|
34
|
+
};
|
|
35
|
+
}),
|
|
36
|
+
})),
|
|
37
|
+
registerFirmwareUpdateHostBinding: jest.fn(binding => {
|
|
38
|
+
artifactReader = binding.artifactReader;
|
|
39
|
+
return 7;
|
|
40
|
+
}),
|
|
41
|
+
unregisterFirmwareUpdateHostBinding,
|
|
42
|
+
} as unknown as Pick<
|
|
43
|
+
CoreApi,
|
|
44
|
+
| 'prepareFirmwareUpdatePlan'
|
|
45
|
+
| 'registerFirmwareUpdateHostBinding'
|
|
46
|
+
| 'unregisterFirmwareUpdateHostBinding'
|
|
47
|
+
>;
|
|
48
|
+
const plan = {
|
|
49
|
+
schemaVersion: 2,
|
|
50
|
+
planDigest: 'a'.repeat(64),
|
|
51
|
+
executor: 'v4',
|
|
52
|
+
deviceIdentity: 'device-id',
|
|
53
|
+
deviceModel: 'pro2',
|
|
54
|
+
firmwareType: 0,
|
|
55
|
+
platform: 'web',
|
|
56
|
+
targetsToUpdate: ['boot', 'resource'],
|
|
57
|
+
artifacts: [
|
|
58
|
+
{
|
|
59
|
+
artifactId: 'component:boot',
|
|
60
|
+
role: 'component',
|
|
61
|
+
target: 'boot',
|
|
62
|
+
url: 'https://example.com/boot.okpkg',
|
|
63
|
+
container: 'raw',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
artifactId: 'resource:archive',
|
|
67
|
+
role: 'resourceBundle',
|
|
68
|
+
target: 'resource',
|
|
69
|
+
url: 'https://example.com/resource.zip',
|
|
70
|
+
container: 'zip',
|
|
71
|
+
logicalName: 'resource-archive',
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
} as FirmwareUpdatePlan;
|
|
75
|
+
|
|
76
|
+
const host = prepareFirmwareUpdateV4MemoryHost({
|
|
77
|
+
sdk,
|
|
78
|
+
plan,
|
|
79
|
+
artifacts: [
|
|
80
|
+
{ artifactId: 'component:boot', binary: componentBinary },
|
|
81
|
+
{
|
|
82
|
+
artifactId: 'resource:archive',
|
|
83
|
+
binary: archiveBinary,
|
|
84
|
+
materializedEntries: [{ entryName: 'manifest.json', binary: manifestBinary }],
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(host.hostBindingGeneration).toBe(7);
|
|
90
|
+
expect(host.componentArtifacts.boot?.size).toBe(componentBinary.byteLength);
|
|
91
|
+
const opened = await artifactReader!.open({
|
|
92
|
+
artifactRef: host.componentArtifacts.boot!.artifactRef,
|
|
93
|
+
});
|
|
94
|
+
const chunk = await artifactReader!.read({
|
|
95
|
+
readerId: opened.readerId,
|
|
96
|
+
offset: 1,
|
|
97
|
+
length: 2,
|
|
98
|
+
});
|
|
99
|
+
expect(Array.from(new Uint8Array(chunk.data))).toEqual([2, 3]);
|
|
100
|
+
await artifactReader!.close({ readerId: opened.readerId });
|
|
101
|
+
|
|
102
|
+
host.release();
|
|
103
|
+
expect(unregisterFirmwareUpdateHostBinding).toHaveBeenCalledWith(7);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -297,12 +297,6 @@ describe('buildFirmwareUpdatePlan', () => {
|
|
|
297
297
|
forceTarget: 'firmware' as const,
|
|
298
298
|
release: {
|
|
299
299
|
components: {},
|
|
300
|
-
resourceBundles: [
|
|
301
|
-
{
|
|
302
|
-
name: 'images',
|
|
303
|
-
url: 'https://firmware.onekey.so/pro2/images.okpkg',
|
|
304
|
-
},
|
|
305
|
-
],
|
|
306
300
|
},
|
|
307
301
|
},
|
|
308
302
|
{
|
|
@@ -325,41 +319,21 @@ describe('buildFirmwareUpdatePlan', () => {
|
|
|
325
319
|
}
|
|
326
320
|
);
|
|
327
321
|
|
|
328
|
-
test
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
},
|
|
322
|
+
test('limits a Pro2 firmware force to component artifacts', () => {
|
|
323
|
+
const plan = buildFirmwareUpdatePlan(
|
|
324
|
+
createProtocolV2ForceInput(['firmware'], {
|
|
325
|
+
components: {
|
|
326
|
+
applicationP1: {
|
|
327
|
+
target: 'APPLICATION_P1',
|
|
328
|
+
url: 'https://firmware.onekey.so/pro2/application-p1.bin',
|
|
349
329
|
},
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
url: 'https://firmware.onekey.so/pro2/images.okpkg',
|
|
354
|
-
},
|
|
355
|
-
],
|
|
356
|
-
})
|
|
357
|
-
);
|
|
330
|
+
},
|
|
331
|
+
})
|
|
332
|
+
);
|
|
358
333
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
);
|
|
334
|
+
expect(plan.artifacts.map(artifact => artifact.artifactId)).toEqual(['component:app_v1']);
|
|
335
|
+
expect(plan.targetsToUpdate).toEqual(['app_v1']);
|
|
336
|
+
});
|
|
363
337
|
|
|
364
338
|
test('rejects a prepared native plan without a stable device identity', () => {
|
|
365
339
|
const features = createFeatures({
|
|
@@ -384,7 +358,7 @@ describe('buildFirmwareUpdatePlan', () => {
|
|
|
384
358
|
).toThrow('requires a stable device identity');
|
|
385
359
|
});
|
|
386
360
|
|
|
387
|
-
test('maps the Pro2 component order
|
|
361
|
+
test('maps the Pro2 component order without downloading artifacts', () => {
|
|
388
362
|
const plan = buildFirmwareUpdatePlan({
|
|
389
363
|
features: createFeatures({ deviceType: EDeviceType.Pro2 }),
|
|
390
364
|
firmwareType: EFirmwareType.Universal,
|
|
@@ -409,12 +383,6 @@ describe('buildFirmwareUpdatePlan', () => {
|
|
|
409
383
|
url: 'https://firmware.onekey.so/pro2/se01.bin',
|
|
410
384
|
},
|
|
411
385
|
},
|
|
412
|
-
resourceBundles: [
|
|
413
|
-
{
|
|
414
|
-
name: 'images',
|
|
415
|
-
url: 'https://firmware.onekey.so/pro2/images.okpkg',
|
|
416
|
-
},
|
|
417
|
-
],
|
|
418
386
|
},
|
|
419
387
|
},
|
|
420
388
|
ble: noUpdate,
|
|
@@ -426,9 +394,8 @@ describe('buildFirmwareUpdatePlan', () => {
|
|
|
426
394
|
'component:boot',
|
|
427
395
|
'component:app_v1',
|
|
428
396
|
'component:se01',
|
|
429
|
-
'resourceBundle:images',
|
|
430
397
|
]);
|
|
431
|
-
expect(plan.targetsToUpdate).toEqual(['boot', 'app_v1', 'se01'
|
|
398
|
+
expect(plan.targetsToUpdate).toEqual(['boot', 'app_v1', 'se01']);
|
|
432
399
|
expect(plan.artifacts[1]).toEqual(
|
|
433
400
|
expect.objectContaining({
|
|
434
401
|
expectedSize: 1024,
|
|
@@ -477,28 +444,6 @@ describe('buildFirmwareUpdatePlan', () => {
|
|
|
477
444
|
},
|
|
478
445
|
expectedError: 'duplicates target app_v1',
|
|
479
446
|
},
|
|
480
|
-
{
|
|
481
|
-
label: 'resource bundle name',
|
|
482
|
-
release: {
|
|
483
|
-
components: {
|
|
484
|
-
application: {
|
|
485
|
-
target: 'APPLICATION_P1',
|
|
486
|
-
url: 'https://firmware.onekey.so/pro2/application.bin',
|
|
487
|
-
},
|
|
488
|
-
},
|
|
489
|
-
resourceBundles: [
|
|
490
|
-
{
|
|
491
|
-
name: 'images',
|
|
492
|
-
url: 'https://firmware.onekey.so/pro2/images.okpkg',
|
|
493
|
-
},
|
|
494
|
-
{
|
|
495
|
-
name: 'images',
|
|
496
|
-
url: 'https://firmware.onekey.so/pro2/images-duplicate.okpkg',
|
|
497
|
-
},
|
|
498
|
-
],
|
|
499
|
-
},
|
|
500
|
-
expectedError: 'duplicates name images',
|
|
501
|
-
},
|
|
502
447
|
])('rejects a duplicate Pro2 $label', ({ release, expectedError }) => {
|
|
503
448
|
expect(() =>
|
|
504
449
|
buildFirmwareUpdatePlan({
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
buildFirmwareUpdatePlan,
|
|
5
|
+
digestFirmwareUpdateContract,
|
|
6
|
+
} from '../../src/api/firmware/FirmwareUpdatePlan';
|
|
4
7
|
import {
|
|
5
8
|
assertFirmwareUpdatePreparedPlanBinding,
|
|
6
9
|
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
@@ -209,6 +212,55 @@ describe('FirmwareUpdatePreparedPlan', () => {
|
|
|
209
212
|
).toThrow('entry name is invalid');
|
|
210
213
|
});
|
|
211
214
|
|
|
215
|
+
test('rejects duplicate materialized entry names during standalone validation', () => {
|
|
216
|
+
const plan = buildPlan();
|
|
217
|
+
const prepared = prepareFirmwareUpdatePlan({
|
|
218
|
+
plan,
|
|
219
|
+
leaseRef: 'fwlease:00000000-0000-4000-8000-000000000001',
|
|
220
|
+
artifacts: [
|
|
221
|
+
{
|
|
222
|
+
artifactId: 'firmware',
|
|
223
|
+
artifact: artifact('a'.repeat(64), 4),
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
artifactId: 'resource',
|
|
227
|
+
artifact: artifact('b'.repeat(64), 8),
|
|
228
|
+
materializedEntries: [
|
|
229
|
+
{
|
|
230
|
+
entryName: 'resource/index.bin',
|
|
231
|
+
artifact: artifact('c'.repeat(64), 2),
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
});
|
|
237
|
+
const resourceArtifact = prepared.artifacts.find(item => item.target === 'resource')!;
|
|
238
|
+
const tamperedWithoutDigest = {
|
|
239
|
+
...prepared,
|
|
240
|
+
artifacts: prepared.artifacts.map(item =>
|
|
241
|
+
item === resourceArtifact
|
|
242
|
+
? {
|
|
243
|
+
...item,
|
|
244
|
+
materializedEntries: [
|
|
245
|
+
...resourceArtifact.materializedEntries!,
|
|
246
|
+
{
|
|
247
|
+
entryName: 'other/index.bin',
|
|
248
|
+
artifact: artifact('d'.repeat(64), 2),
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
}
|
|
252
|
+
: item
|
|
253
|
+
),
|
|
254
|
+
};
|
|
255
|
+
const { preparedPlanDigest: _preparedPlanDigest, ...digestInput } = tamperedWithoutDigest;
|
|
256
|
+
const tampered = {
|
|
257
|
+
...tamperedWithoutDigest,
|
|
258
|
+
preparedPlanDigest: digestFirmwareUpdateContract(digestInput),
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
expect(() => validateFirmwareUpdatePreparedPlan(tampered)).toThrow('duplicate entry names');
|
|
262
|
+
});
|
|
263
|
+
|
|
212
264
|
test('rejects unknown fields even when a caller presents a plan-shaped object', () => {
|
|
213
265
|
const plan = buildPlan();
|
|
214
266
|
expect(() =>
|