@onekeyfe/hd-core 1.2.0-alpha.141 → 1.2.0-alpha.143
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__/device-lifecycle-events.test.ts +23 -1
- package/__tests__/protocol-v2-resources.test.ts +35 -89
- package/__tests__/protocol-v2.test.ts +56 -196
- package/dist/api/FirmwareUpdateV4.d.ts +0 -1
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +121 -282
- package/dist/protocols/protocol-v2/resources.d.ts +11 -7
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/types/settings.d.ts +0 -13
- package/dist/types/settings.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +66 -275
- package/src/core/index.ts +5 -2
- package/src/index.ts +0 -4
- package/src/protocols/protocol-v2/resources.ts +92 -102
- package/src/types/settings.ts +0 -15
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { EDeviceType, ERRORS, HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
|
|
2
2
|
import { DeviceType, TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
initConnector,
|
|
6
|
+
initCore,
|
|
7
|
+
isMissingDetectedProtocolV2Error,
|
|
8
|
+
isRetryableBleProtocolV2ProbeError,
|
|
9
|
+
} from '../src/core';
|
|
5
10
|
import { DataManager } from '../src/data-manager';
|
|
6
11
|
import TransportManager from '../src/data-manager/TransportManager';
|
|
7
12
|
import { Device } from '../src/device/Device';
|
|
@@ -269,6 +274,23 @@ describe('public device lifecycle events', () => {
|
|
|
269
274
|
}
|
|
270
275
|
);
|
|
271
276
|
|
|
277
|
+
test.each([
|
|
278
|
+
[HardwareErrorCode.RuntimeError, true],
|
|
279
|
+
[HardwareErrorCode.BleDeviceBondError, false],
|
|
280
|
+
] as const)(
|
|
281
|
+
'retries a Protocol V2 probe mismatch with error code %s: %s',
|
|
282
|
+
(errorCode, expected) => {
|
|
283
|
+
const method = { payload: { connectProtocol: 'V2' } } as never;
|
|
284
|
+
const error = {
|
|
285
|
+
errorCode,
|
|
286
|
+
message:
|
|
287
|
+
'Device protocol mismatch: expected V2, but device did not respond to expected protocol',
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
expect(isRetryableBleProtocolV2ProbeError(method, error)).toBe(expected);
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
|
|
272
294
|
test('converts an internal transport disconnect into a public KnownDevice snapshot', () => {
|
|
273
295
|
jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
|
|
274
296
|
core = initCore();
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import { sha256 } from '@noble/hashes/sha256';
|
|
3
2
|
import { EDeviceType, EFirmwareType, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
4
3
|
|
|
5
4
|
import { DataManager } from '../src/data-manager';
|
|
6
5
|
import {
|
|
7
|
-
|
|
6
|
+
parseProtocolV2ResourcePackage,
|
|
8
7
|
parseProtocolV2Resources,
|
|
9
8
|
} from '../src/protocols/protocol-v2/resources';
|
|
10
9
|
|
|
@@ -16,9 +15,6 @@ jest.mock('../src/data/config', () => ({
|
|
|
16
15
|
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0-test/',
|
|
17
16
|
}));
|
|
18
17
|
|
|
19
|
-
const bytesToHex = (bytes: Uint8Array) =>
|
|
20
|
-
Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
21
|
-
|
|
22
18
|
const resourceSource = {
|
|
23
19
|
archiveUrl: 'https://example.com/resource/pro2-resource.zip',
|
|
24
20
|
archiveSha256: 'a'.repeat(64),
|
|
@@ -31,65 +27,21 @@ const neoResourceSource = {
|
|
|
31
27
|
archiveSize: 12_345_678,
|
|
32
28
|
};
|
|
33
29
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
[
|
|
50
|
-
'bundles/translations/translations-build.okpkg',
|
|
51
|
-
'translations.okpkg',
|
|
52
|
-
'vol0:/bundles/translations/translations.okpkg',
|
|
53
|
-
],
|
|
54
|
-
[
|
|
55
|
-
'loaders/bootloader/boot_resource-build.okpkg',
|
|
56
|
-
'boot_resource.okpkg',
|
|
57
|
-
'vol0:/loaders/bootloader/boot_resource.okpkg',
|
|
58
|
-
],
|
|
59
|
-
['loaders/rom/params-build.okpkg', 'params.okpkg', 'vol0:/loaders/rom/params.okpkg'],
|
|
60
|
-
].map(([archive_path, original_name, device_path], index) => {
|
|
61
|
-
const binary = new Uint8Array([index + 1]).buffer;
|
|
62
|
-
return {
|
|
63
|
-
archive_path,
|
|
64
|
-
original_name,
|
|
65
|
-
device_path,
|
|
66
|
-
size: 1,
|
|
67
|
-
sha256: bytesToHex(sha256(new Uint8Array(binary))),
|
|
68
|
-
signed: true,
|
|
69
|
-
sig_algo: device_path.startsWith('vol0:/bundles/') ? 'ed25519' : 'mldsa65',
|
|
70
|
-
payload_version: '1.0.0',
|
|
71
|
-
binary,
|
|
72
|
-
};
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const resourceManifest = {
|
|
76
|
-
schema: 1,
|
|
77
|
-
artifact_name: 'pro2-resource-build',
|
|
78
|
-
release_name: 'resource-build',
|
|
79
|
-
variant: 'resource',
|
|
80
|
-
commit: 'a'.repeat(40),
|
|
81
|
-
short_sha: 'aaaaaaa',
|
|
82
|
-
timestamp_utc: '20260807_091424',
|
|
83
|
-
core_version: '1.0.0',
|
|
84
|
-
key_set: 'dev',
|
|
85
|
-
device_root: 'vol0:',
|
|
86
|
-
restore_mode: 'bootloader_update',
|
|
87
|
-
trees: [
|
|
88
|
-
{ path: 'bundles', device: 'vol0:/bundles' },
|
|
89
|
-
{ path: 'loaders/bootloader', device: 'vol0:/loaders/bootloader' },
|
|
90
|
-
{ path: 'loaders/rom', device: 'vol0:/loaders/rom' },
|
|
91
|
-
],
|
|
92
|
-
files: manifestFiles.map(({ binary: _binary, ...file }) => file),
|
|
30
|
+
const createResourcePackage = (devicePath: string) => {
|
|
31
|
+
const headerSize = 0x5f90;
|
|
32
|
+
const bytes = new Uint8Array(headerSize + 1);
|
|
33
|
+
bytes.set(new TextEncoder().encode('OKPP'), 0);
|
|
34
|
+
bytes.set(new TextEncoder().encode('RESC'), 0x08);
|
|
35
|
+
bytes.set(new TextEncoder().encode(devicePath), 0x6c);
|
|
36
|
+
const view = new DataView(bytes.buffer);
|
|
37
|
+
view.setUint32(0x04, 1, true);
|
|
38
|
+
view.setUint32(0x0c, headerSize, true);
|
|
39
|
+
view.setUint32(0x10, 0x010203, true);
|
|
40
|
+
view.setUint32(0x14, 1, true);
|
|
41
|
+
bytes.fill(0x11, 0x200, 0x240);
|
|
42
|
+
bytes.fill(0x22, 0x240, 0x280);
|
|
43
|
+
bytes[headerSize] = 0xaa;
|
|
44
|
+
return bytes;
|
|
93
45
|
};
|
|
94
46
|
|
|
95
47
|
const createSettings = (configFetcher: ConnectSettings['configFetcher']): ConnectSettings =>
|
|
@@ -146,34 +98,36 @@ describe('Pro2 resource configuration', () => {
|
|
|
146
98
|
DataManager.lastCheckTimestamp = 0;
|
|
147
99
|
});
|
|
148
100
|
|
|
149
|
-
test('accepts the
|
|
101
|
+
test('accepts the release archive source', () => {
|
|
150
102
|
expect(parseProtocolV2Resources({ source: resourceSource })).toEqual({
|
|
151
103
|
source: resourceSource,
|
|
152
104
|
});
|
|
153
|
-
expect(parseProtocolV2ResourceManifest(resourceManifest).files).toHaveLength(9);
|
|
154
105
|
});
|
|
155
106
|
|
|
156
|
-
test('
|
|
107
|
+
test('reads the version, hashes, and direct device path from a RESC package', () => {
|
|
157
108
|
expect(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
109
|
+
parseProtocolV2ResourcePackage(createResourcePackage('vol0:/bundles/images.okpkg'))
|
|
110
|
+
).toEqual({
|
|
111
|
+
version: [1, 2, 3],
|
|
112
|
+
payloadLength: 1,
|
|
113
|
+
devicePath: 'vol0:/bundles/images.okpkg',
|
|
114
|
+
payloadHash: '11'.repeat(64),
|
|
115
|
+
headerHash: '22'.repeat(64),
|
|
116
|
+
});
|
|
162
117
|
});
|
|
163
118
|
|
|
164
|
-
test('
|
|
119
|
+
test('accepts the boot resource staging path and rejects paths outside resource roots', () => {
|
|
165
120
|
expect(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
).toEqual({ files: [resourceManifest.files[0]] });
|
|
121
|
+
parseProtocolV2ResourcePackage(
|
|
122
|
+
createResourcePackage('vol0:/loaders/bootloader/boot_resource.okpkg.staging')
|
|
123
|
+
).devicePath
|
|
124
|
+
).toBe('vol0:/loaders/bootloader/boot_resource.okpkg.staging');
|
|
125
|
+
expect(() =>
|
|
126
|
+
parseProtocolV2ResourcePackage(createResourcePackage('vol0:/unexpected/images.okpkg'))
|
|
127
|
+
).toThrow('device path');
|
|
174
128
|
});
|
|
175
129
|
|
|
176
|
-
test('rejects missing
|
|
130
|
+
test('rejects missing or malformed release sources', () => {
|
|
177
131
|
expect(() => parseProtocolV2Resources({})).toThrow('source is required');
|
|
178
132
|
expect(() =>
|
|
179
133
|
parseProtocolV2Resources({
|
|
@@ -193,14 +147,6 @@ describe('Pro2 resource configuration', () => {
|
|
|
193
147
|
source: { ...resourceSource, archiveSize: 0 },
|
|
194
148
|
})
|
|
195
149
|
).toThrow('positive integer');
|
|
196
|
-
expect(() =>
|
|
197
|
-
parseProtocolV2ResourceManifest({
|
|
198
|
-
...resourceManifest,
|
|
199
|
-
files: resourceManifest.files.map((file, index) =>
|
|
200
|
-
index === 0 ? { ...file, archive_path: '../outside.okpkg' } : file
|
|
201
|
-
),
|
|
202
|
-
})
|
|
203
|
-
).toThrow('archive_path');
|
|
204
150
|
});
|
|
205
151
|
|
|
206
152
|
test('applies a validated pre-release config and exposes its archive source', async () => {
|
|
@@ -139,21 +139,29 @@ const createJpegBase64 = (width: number, height: number) => {
|
|
|
139
139
|
|
|
140
140
|
const createProtocolV2OkppBinary = ({
|
|
141
141
|
version = [1, 2, 3],
|
|
142
|
+
devicePath = 'vol0:/bundles/images/images.okpkg',
|
|
142
143
|
payloadHashByte = 0x11,
|
|
143
144
|
headerHashByte = 0x22,
|
|
144
145
|
}: {
|
|
145
146
|
version?: [number, number, number];
|
|
147
|
+
devicePath?: string;
|
|
146
148
|
payloadHashByte?: number;
|
|
147
149
|
headerHashByte?: number;
|
|
148
150
|
} = {}) => {
|
|
149
|
-
const
|
|
151
|
+
const headerSize = 0x5f90;
|
|
152
|
+
const payloadSize = 1;
|
|
153
|
+
const bytes = new Uint8Array(headerSize + payloadSize);
|
|
150
154
|
bytes.set([0x4f, 0x4b, 0x50, 0x50], 0);
|
|
151
155
|
bytes.set([0x52, 0x45, 0x53, 0x43], 0x08);
|
|
152
156
|
const view = new DataView(bytes.buffer);
|
|
153
|
-
view.setUint32(
|
|
157
|
+
view.setUint32(0x04, 1, true);
|
|
158
|
+
view.setUint32(0x0c, headerSize, true);
|
|
154
159
|
view.setUint32(0x10, version[0] * 0x10000 + version[1] * 0x100 + version[2], true);
|
|
160
|
+
view.setUint32(0x14, payloadSize, true);
|
|
161
|
+
bytes.set(new TextEncoder().encode(devicePath), 0x6c);
|
|
155
162
|
bytes.fill(payloadHashByte, 0x200, 0x240);
|
|
156
163
|
bytes.fill(headerHashByte, 0x240, 0x280);
|
|
164
|
+
bytes[headerSize] = 0xaa;
|
|
157
165
|
return bytes.buffer;
|
|
158
166
|
};
|
|
159
167
|
|
|
@@ -6011,7 +6019,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6011
6019
|
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
6012
6020
|
});
|
|
6013
6021
|
|
|
6014
|
-
test('
|
|
6022
|
+
test('uses the device firmware version for P1 and install completion for unobservable P2', () => {
|
|
6015
6023
|
const method = new FirmwareUpdateV4({
|
|
6016
6024
|
id: 1,
|
|
6017
6025
|
payload: {
|
|
@@ -6019,7 +6027,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6019
6027
|
platform: 'desktop',
|
|
6020
6028
|
targetsToUpdate: ['app_v1', 'app_v2'],
|
|
6021
6029
|
expectedTargetVersions: {
|
|
6022
|
-
app_v1: '
|
|
6030
|
+
app_v1: '3.0.0',
|
|
6023
6031
|
app_v2: '2.0.0',
|
|
6024
6032
|
},
|
|
6025
6033
|
},
|
|
@@ -6035,7 +6043,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6035
6043
|
expect(() => (method as any).assertExpectedProtocolV2Versions()).not.toThrow();
|
|
6036
6044
|
});
|
|
6037
6045
|
|
|
6038
|
-
test('uses
|
|
6046
|
+
test('uses the firmware version for P1 and the optional DeviceInfo version for P2', () => {
|
|
6039
6047
|
const method = new FirmwareUpdateV4({
|
|
6040
6048
|
id: 1,
|
|
6041
6049
|
payload: {
|
|
@@ -6050,13 +6058,13 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6050
6058
|
});
|
|
6051
6059
|
method.init();
|
|
6052
6060
|
(method as any).protocolV2LatestFinalFeatures = {
|
|
6053
|
-
major_version:
|
|
6061
|
+
major_version: 1,
|
|
6054
6062
|
minor_version: 0,
|
|
6055
6063
|
patch_version: 0,
|
|
6056
6064
|
};
|
|
6057
6065
|
(method as any).protocolV2LatestFinalDeviceInfo = {
|
|
6058
6066
|
main_mcu: {
|
|
6059
|
-
application: { version: '
|
|
6067
|
+
application: { version: '9.9.9' },
|
|
6060
6068
|
application_data: { version: '2.0.0' },
|
|
6061
6069
|
},
|
|
6062
6070
|
};
|
|
@@ -6079,7 +6087,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6079
6087
|
});
|
|
6080
6088
|
method.init();
|
|
6081
6089
|
(method as any).protocolV2LatestFinalFeatures = {
|
|
6082
|
-
major_version:
|
|
6090
|
+
major_version: 1,
|
|
6083
6091
|
minor_version: 0,
|
|
6084
6092
|
patch_version: 0,
|
|
6085
6093
|
};
|
|
@@ -6095,7 +6103,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6095
6103
|
);
|
|
6096
6104
|
});
|
|
6097
6105
|
|
|
6098
|
-
test('rejects unobservable
|
|
6106
|
+
test('rejects an unobservable P2 version without P2 completion evidence', () => {
|
|
6099
6107
|
const method = new FirmwareUpdateV4({
|
|
6100
6108
|
id: 1,
|
|
6101
6109
|
payload: {
|
|
@@ -6103,7 +6111,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6103
6111
|
platform: 'desktop',
|
|
6104
6112
|
targetsToUpdate: ['app_v1', 'app_v2'],
|
|
6105
6113
|
expectedTargetVersions: {
|
|
6106
|
-
app_v1: '
|
|
6114
|
+
app_v1: '3.0.0',
|
|
6107
6115
|
app_v2: '2.0.0',
|
|
6108
6116
|
},
|
|
6109
6117
|
},
|
|
@@ -6118,7 +6126,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6118
6126
|
(method as any).protocolV2CompletedTargetIds = new Set([4]);
|
|
6119
6127
|
|
|
6120
6128
|
expect(() => (method as any).assertExpectedProtocolV2Versions()).toThrow(
|
|
6121
|
-
'target
|
|
6129
|
+
'target app_v2 has no observable final version after status fallback'
|
|
6122
6130
|
);
|
|
6123
6131
|
});
|
|
6124
6132
|
|
|
@@ -7228,7 +7236,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7228
7236
|
});
|
|
7229
7237
|
});
|
|
7230
7238
|
|
|
7231
|
-
test('maps a prepared resource archive
|
|
7239
|
+
test('maps self-describing packages from a prepared resource archive', async () => {
|
|
7232
7240
|
const method = new FirmwareUpdateV4({
|
|
7233
7241
|
id: 1,
|
|
7234
7242
|
payload: {
|
|
@@ -7236,60 +7244,18 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7236
7244
|
},
|
|
7237
7245
|
});
|
|
7238
7246
|
const imagesBinary = createProtocolV2OkppBinary();
|
|
7239
|
-
const bootBinary = createProtocolV2OkppBinary({
|
|
7247
|
+
const bootBinary = createProtocolV2OkppBinary({
|
|
7248
|
+
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg.staging',
|
|
7249
|
+
payloadHashByte: 0x33,
|
|
7250
|
+
});
|
|
7240
7251
|
const imagesSha256 = bytesToHex(sha256(new Uint8Array(imagesBinary)));
|
|
7241
7252
|
const bootSha256 = bytesToHex(sha256(new Uint8Array(bootBinary)));
|
|
7242
|
-
const manifest = {
|
|
7243
|
-
schema: 1,
|
|
7244
|
-
artifact_name: 'pro2-resource',
|
|
7245
|
-
release_name: 'test-release',
|
|
7246
|
-
variant: 'resource',
|
|
7247
|
-
commit: 'abc123',
|
|
7248
|
-
short_sha: 'abc123',
|
|
7249
|
-
timestamp_utc: '2026-08-09T00:00:00Z',
|
|
7250
|
-
core_version: '1.0.0',
|
|
7251
|
-
key_set: 'production',
|
|
7252
|
-
device_root: 'vol0:',
|
|
7253
|
-
restore_mode: 'bootloader_update',
|
|
7254
|
-
trees: [{ path: 'bundles', device: 'pro2' }],
|
|
7255
|
-
files: [
|
|
7256
|
-
{
|
|
7257
|
-
archive_path: 'bundles/images/images.okpkg',
|
|
7258
|
-
original_name: 'images.okpkg',
|
|
7259
|
-
device_path: 'vol0:/bundles/images/images.okpkg',
|
|
7260
|
-
size: imagesBinary.byteLength,
|
|
7261
|
-
sha256: imagesSha256,
|
|
7262
|
-
signed: true,
|
|
7263
|
-
sig_algo: 'ed25519',
|
|
7264
|
-
payload_version: '1.2.3',
|
|
7265
|
-
},
|
|
7266
|
-
{
|
|
7267
|
-
archive_path: 'loaders/bootloader/boot_resource.okpkg',
|
|
7268
|
-
original_name: 'boot_resource.okpkg',
|
|
7269
|
-
device_path: 'vol0:/loaders/bootloader/boot_resource.okpkg',
|
|
7270
|
-
size: bootBinary.byteLength,
|
|
7271
|
-
sha256: bootSha256,
|
|
7272
|
-
signed: true,
|
|
7273
|
-
sig_algo: 'ed25519',
|
|
7274
|
-
payload_version: '1.2.3',
|
|
7275
|
-
},
|
|
7276
|
-
],
|
|
7277
|
-
};
|
|
7278
|
-
const manifestBinary = new TextEncoder().encode(JSON.stringify(manifest)).buffer;
|
|
7279
7253
|
const zip = new JSZip();
|
|
7280
|
-
zip.file('manifest.json', manifestBinary);
|
|
7281
7254
|
zip.file('bundles/images/images.okpkg', imagesBinary);
|
|
7282
7255
|
zip.file('loaders/bootloader/boot_resource.okpkg', bootBinary);
|
|
7256
|
+
zip.file('manifest.json', '{"ignored":true}');
|
|
7283
7257
|
const archiveBinary = await zip.generateAsync({ type: 'arraybuffer' });
|
|
7284
7258
|
const preparedEntries = [
|
|
7285
|
-
{
|
|
7286
|
-
entryName: 'pro2-resource/manifest.json',
|
|
7287
|
-
artifact: {
|
|
7288
|
-
artifactRef: 'manifest',
|
|
7289
|
-
size: manifestBinary.byteLength,
|
|
7290
|
-
sha256: bytesToHex(sha256(new Uint8Array(manifestBinary))),
|
|
7291
|
-
},
|
|
7292
|
-
},
|
|
7293
7259
|
{
|
|
7294
7260
|
entryName: 'pro2-resource/bundles/images/images.okpkg',
|
|
7295
7261
|
artifact: {
|
|
@@ -7352,7 +7318,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7352
7318
|
}),
|
|
7353
7319
|
expect.objectContaining({
|
|
7354
7320
|
name: 'boot_resource.okpkg',
|
|
7355
|
-
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg',
|
|
7321
|
+
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg.staging',
|
|
7356
7322
|
version: [1, 2, 3],
|
|
7357
7323
|
}),
|
|
7358
7324
|
]);
|
|
@@ -7398,9 +7364,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7398
7364
|
},
|
|
7399
7365
|
materializedEntries: [
|
|
7400
7366
|
{
|
|
7401
|
-
entryName: '
|
|
7367
|
+
entryName: 'bundles/images/images.okpkg',
|
|
7402
7368
|
artifact: {
|
|
7403
|
-
artifactRef: 'resource-
|
|
7369
|
+
artifactRef: 'resource-package',
|
|
7404
7370
|
size: 2,
|
|
7405
7371
|
sha256: 'b'.repeat(64),
|
|
7406
7372
|
},
|
|
@@ -7600,9 +7566,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7600
7566
|
},
|
|
7601
7567
|
materializedEntries: [
|
|
7602
7568
|
{
|
|
7603
|
-
entryName: '
|
|
7569
|
+
entryName: 'bundles/images/images.okpkg',
|
|
7604
7570
|
artifact: {
|
|
7605
|
-
artifactRef: 'prepared-resource-
|
|
7571
|
+
artifactRef: 'prepared-resource-package',
|
|
7606
7572
|
size: 2,
|
|
7607
7573
|
sha256: 'b'.repeat(64),
|
|
7608
7574
|
},
|
|
@@ -7659,48 +7625,15 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7659
7625
|
});
|
|
7660
7626
|
|
|
7661
7627
|
test('converts a local resource ZIP into a local PreparedPlan without matching a remote release', async () => {
|
|
7662
|
-
const imagesBinary =
|
|
7663
|
-
const bootResourceBinary =
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
release_name: 'local-development',
|
|
7668
|
-
variant: 'resource',
|
|
7669
|
-
commit: 'local',
|
|
7670
|
-
short_sha: 'local',
|
|
7671
|
-
timestamp_utc: '2026-08-09T00:00:00Z',
|
|
7672
|
-
core_version: '1.0.0',
|
|
7673
|
-
key_set: 'development',
|
|
7674
|
-
device_root: 'vol0:',
|
|
7675
|
-
restore_mode: 'bootloader_update',
|
|
7676
|
-
trees: [{ path: 'bundles', device: 'pro2' }],
|
|
7677
|
-
files: [
|
|
7678
|
-
{
|
|
7679
|
-
archive_path: 'bundles/images/images.okpkg',
|
|
7680
|
-
original_name: 'images.okpkg',
|
|
7681
|
-
device_path: 'vol0:/bundles/images/images.okpkg',
|
|
7682
|
-
size: imagesBinary.byteLength,
|
|
7683
|
-
sha256: bytesToHex(sha256(new Uint8Array(imagesBinary))),
|
|
7684
|
-
signed: true,
|
|
7685
|
-
sig_algo: 'ed25519',
|
|
7686
|
-
payload_version: '1.0.0',
|
|
7687
|
-
},
|
|
7688
|
-
{
|
|
7689
|
-
archive_path: 'loaders/bootloader/boot_resource.okpkg',
|
|
7690
|
-
original_name: 'boot_resource.okpkg',
|
|
7691
|
-
device_path: 'vol0:/loaders/bootloader/boot_resource.okpkg',
|
|
7692
|
-
size: bootResourceBinary.byteLength,
|
|
7693
|
-
sha256: bytesToHex(sha256(new Uint8Array(bootResourceBinary))),
|
|
7694
|
-
signed: true,
|
|
7695
|
-
sig_algo: 'ed25519',
|
|
7696
|
-
payload_version: '1.0.0',
|
|
7697
|
-
},
|
|
7698
|
-
],
|
|
7699
|
-
};
|
|
7628
|
+
const imagesBinary = createProtocolV2OkppBinary({ version: [1, 0, 0] });
|
|
7629
|
+
const bootResourceBinary = createProtocolV2OkppBinary({
|
|
7630
|
+
version: [1, 0, 0],
|
|
7631
|
+
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg.staging',
|
|
7632
|
+
});
|
|
7700
7633
|
const zip = new JSZip();
|
|
7701
|
-
zip.file('pro2-resource/manifest.json', JSON.stringify(manifest));
|
|
7702
7634
|
zip.file('pro2-resource/bundles/images/images.okpkg', imagesBinary);
|
|
7703
7635
|
zip.file('pro2-resource/loaders/bootloader/boot_resource.okpkg', bootResourceBinary);
|
|
7636
|
+
zip.file('pro2-resource/manifest.json', '{"ignored":true}');
|
|
7704
7637
|
zip.file('pro2-resource/build-info.txt', 'ignored');
|
|
7705
7638
|
const resourceArchiveBinary = await zip.generateAsync({ type: 'arraybuffer' });
|
|
7706
7639
|
const applicationP1Binary = new Uint8Array([7, 8, 9]).buffer;
|
|
@@ -7766,7 +7699,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7766
7699
|
}),
|
|
7767
7700
|
expect.objectContaining({
|
|
7768
7701
|
name: 'boot_resource.okpkg',
|
|
7769
|
-
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg',
|
|
7702
|
+
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg.staging',
|
|
7770
7703
|
}),
|
|
7771
7704
|
],
|
|
7772
7705
|
})
|
|
@@ -7777,9 +7710,11 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7777
7710
|
}
|
|
7778
7711
|
});
|
|
7779
7712
|
|
|
7780
|
-
test('accepts a local resource ZIP
|
|
7781
|
-
const imagesBinary =
|
|
7782
|
-
const bootResourceBinary =
|
|
7713
|
+
test('accepts every okpkg in a local resource ZIP and ignores other files', async () => {
|
|
7714
|
+
const imagesBinary = createProtocolV2OkppBinary();
|
|
7715
|
+
const bootResourceBinary = createProtocolV2OkppBinary({
|
|
7716
|
+
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg.staging',
|
|
7717
|
+
});
|
|
7783
7718
|
const zip = new JSZip();
|
|
7784
7719
|
zip.file('pro2-resource/bundles/images/images.okpkg', imagesBinary);
|
|
7785
7720
|
zip.file('pro2-resource/loaders/bootloader/boot_resource.okpkg', bootResourceBinary);
|
|
@@ -7800,61 +7735,21 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7800
7735
|
)
|
|
7801
7736
|
).resolves.toMatchObject({
|
|
7802
7737
|
materializedEntries: [
|
|
7803
|
-
{ entryName: '
|
|
7804
|
-
{ entryName: 'bundles/images/images.okpkg', binary: imagesBinary },
|
|
7738
|
+
{ entryName: 'pro2-resource/bundles/images/images.okpkg', binary: imagesBinary },
|
|
7805
7739
|
{
|
|
7806
|
-
entryName: 'loaders/bootloader/boot_resource.okpkg',
|
|
7740
|
+
entryName: 'pro2-resource/loaders/bootloader/boot_resource.okpkg',
|
|
7807
7741
|
binary: bootResourceBinary,
|
|
7808
7742
|
},
|
|
7809
7743
|
],
|
|
7810
7744
|
});
|
|
7811
7745
|
});
|
|
7812
7746
|
|
|
7813
|
-
test('rejects a local resource ZIP
|
|
7814
|
-
const resourceBinary =
|
|
7815
|
-
const
|
|
7747
|
+
test('rejects a local resource ZIP with duplicate package device paths', async () => {
|
|
7748
|
+
const resourceBinary = createProtocolV2OkppBinary();
|
|
7749
|
+
const duplicateResourceBinary = createProtocolV2OkppBinary({ payloadHashByte: 0x44 });
|
|
7816
7750
|
const zip = new JSZip();
|
|
7817
|
-
zip.file(
|
|
7818
|
-
'manifest.json',
|
|
7819
|
-
JSON.stringify({
|
|
7820
|
-
schema: 1,
|
|
7821
|
-
artifact_name: 'pro2-resource',
|
|
7822
|
-
release_name: 'local-development',
|
|
7823
|
-
variant: 'resource',
|
|
7824
|
-
commit: 'local',
|
|
7825
|
-
short_sha: 'local',
|
|
7826
|
-
timestamp_utc: '2026-08-09T00:00:00Z',
|
|
7827
|
-
core_version: '1.0.0',
|
|
7828
|
-
key_set: 'development',
|
|
7829
|
-
device_root: 'vol0:',
|
|
7830
|
-
restore_mode: 'bootloader_update',
|
|
7831
|
-
trees: [{ path: 'bundles', device: 'pro2' }],
|
|
7832
|
-
files: [
|
|
7833
|
-
{
|
|
7834
|
-
archive_path: 'bundles/images/images.okpkg',
|
|
7835
|
-
original_name: 'images.okpkg',
|
|
7836
|
-
device_path: 'vol0:/bundles/images/images.okpkg',
|
|
7837
|
-
size: resourceBinary.byteLength,
|
|
7838
|
-
sha256: '0'.repeat(64),
|
|
7839
|
-
signed: true,
|
|
7840
|
-
sig_algo: 'ed25519',
|
|
7841
|
-
payload_version: '1.0.0',
|
|
7842
|
-
},
|
|
7843
|
-
{
|
|
7844
|
-
archive_path: 'loaders/bootloader/boot_resource.okpkg',
|
|
7845
|
-
original_name: 'boot_resource.okpkg',
|
|
7846
|
-
device_path: 'vol0:/loaders/bootloader/boot_resource.okpkg',
|
|
7847
|
-
size: bootResourceBinary.byteLength,
|
|
7848
|
-
sha256: bytesToHex(sha256(new Uint8Array(bootResourceBinary))),
|
|
7849
|
-
signed: true,
|
|
7850
|
-
sig_algo: 'ed25519',
|
|
7851
|
-
payload_version: '1.0.0',
|
|
7852
|
-
},
|
|
7853
|
-
],
|
|
7854
|
-
})
|
|
7855
|
-
);
|
|
7856
7751
|
zip.file('bundles/images/images.okpkg', resourceBinary);
|
|
7857
|
-
zip.file('
|
|
7752
|
+
zip.file('copies/images.okpkg', duplicateResourceBinary);
|
|
7858
7753
|
const method = new FirmwareUpdateV4({
|
|
7859
7754
|
id: 1,
|
|
7860
7755
|
payload: {
|
|
@@ -7869,9 +7764,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7869
7764
|
(method as any).prepareProtocolV2LocalResourceArchive(
|
|
7870
7765
|
await zip.generateAsync({ type: 'arraybuffer' })
|
|
7871
7766
|
)
|
|
7872
|
-
).rejects.toThrow(
|
|
7873
|
-
'Protocol V2 local resource file does not match manifest: bundles/images/images.okpkg'
|
|
7874
|
-
);
|
|
7767
|
+
).rejects.toThrow('duplicate device path: vol0:/bundles/images/images.okpkg');
|
|
7875
7768
|
});
|
|
7876
7769
|
|
|
7877
7770
|
test('maps malformed resource ZIPs to a typed firmware preparation error', async () => {
|
|
@@ -7901,40 +7794,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7901
7794
|
test('rejects an oversized ZIP entry before allocating its decompressed bytes', async () => {
|
|
7902
7795
|
const extractEntry = jest.fn();
|
|
7903
7796
|
const oversizedFileSize = 257 * 1024 * 1024;
|
|
7904
|
-
const manifestBinary = new TextEncoder().encode(
|
|
7905
|
-
JSON.stringify({
|
|
7906
|
-
files: [
|
|
7907
|
-
{
|
|
7908
|
-
archive_path: 'loaders/bootloader/boot_resource.okpkg',
|
|
7909
|
-
original_name: 'boot_resource.okpkg',
|
|
7910
|
-
device_path: 'vol0:/loaders/bootloader/boot_resource.okpkg',
|
|
7911
|
-
size: oversizedFileSize,
|
|
7912
|
-
sha256: '0'.repeat(64),
|
|
7913
|
-
signed: true,
|
|
7914
|
-
sig_algo: 'ed25519',
|
|
7915
|
-
},
|
|
7916
|
-
{
|
|
7917
|
-
archive_path: 'bundles/images/images.okpkg',
|
|
7918
|
-
original_name: 'images.okpkg',
|
|
7919
|
-
device_path: 'vol0:/bundles/images/images.okpkg',
|
|
7920
|
-
size: oversizedFileSize,
|
|
7921
|
-
sha256: '0'.repeat(64),
|
|
7922
|
-
signed: true,
|
|
7923
|
-
sig_algo: 'ed25519',
|
|
7924
|
-
},
|
|
7925
|
-
],
|
|
7926
|
-
})
|
|
7927
|
-
);
|
|
7928
7797
|
const zipFiles = {
|
|
7929
|
-
'manifest.json': {
|
|
7930
|
-
name: 'manifest.json',
|
|
7931
|
-
dir: false,
|
|
7932
|
-
_data: {
|
|
7933
|
-
compressedSize: manifestBinary.byteLength,
|
|
7934
|
-
uncompressedSize: manifestBinary.byteLength,
|
|
7935
|
-
},
|
|
7936
|
-
async: jest.fn().mockResolvedValue(manifestBinary.buffer),
|
|
7937
|
-
},
|
|
7938
7798
|
'loaders/bootloader/boot_resource.okpkg': {
|
|
7939
7799
|
name: 'loaders/bootloader/boot_resource.okpkg',
|
|
7940
7800
|
dir: false,
|
|
@@ -7965,7 +7825,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7965
7825
|
try {
|
|
7966
7826
|
await expect(
|
|
7967
7827
|
(method as any).prepareProtocolV2LocalResourceArchive(new Uint8Array([1]).buffer)
|
|
7968
|
-
).rejects.toThrow('
|
|
7828
|
+
).rejects.toThrow('resource package size is invalid');
|
|
7969
7829
|
expect(extractEntry).not.toHaveBeenCalled();
|
|
7970
7830
|
} finally {
|
|
7971
7831
|
loadSpy.mockRestore();
|
|
@@ -8117,17 +7977,17 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
8117
7977
|
readAt: jest.fn(),
|
|
8118
7978
|
close: jest.fn(),
|
|
8119
7979
|
},
|
|
8120
|
-
devicePath: 'vol0:/
|
|
7980
|
+
devicePath: 'vol0:/bundles/images/images.okpkg',
|
|
8121
7981
|
},
|
|
8122
7982
|
],
|
|
8123
7983
|
});
|
|
8124
7984
|
|
|
8125
7985
|
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenCalledTimes(1);
|
|
8126
7986
|
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenCalledWith(
|
|
8127
|
-
expect.objectContaining({ filePath: 'vol0:/
|
|
7987
|
+
expect.objectContaining({ filePath: 'vol0:/bundles/images/images.okpkg' })
|
|
8128
7988
|
);
|
|
8129
7989
|
expect((method as any).verifyProtocolV2StagedFile).toHaveBeenCalledWith(
|
|
8130
|
-
'vol0:/
|
|
7990
|
+
'vol0:/bundles/images/images.okpkg',
|
|
8131
7991
|
3
|
|
8132
7992
|
);
|
|
8133
7993
|
expect((method as any).protocolV2StartFirmwareUpdate).not.toHaveBeenCalled();
|
|
@@ -8162,7 +8022,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
8162
8022
|
readAt: jest.fn(),
|
|
8163
8023
|
close: jest.fn(),
|
|
8164
8024
|
},
|
|
8165
|
-
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg',
|
|
8025
|
+
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg.staging',
|
|
8166
8026
|
},
|
|
8167
8027
|
],
|
|
8168
8028
|
});
|
|
@@ -8195,7 +8055,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
8195
8055
|
readAt: jest.fn(),
|
|
8196
8056
|
close: jest.fn(),
|
|
8197
8057
|
},
|
|
8198
|
-
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg',
|
|
8058
|
+
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg.staging',
|
|
8199
8059
|
version: [1, 0, 0],
|
|
8200
8060
|
payloadHash: '11'.repeat(64),
|
|
8201
8061
|
headerHash: '22'.repeat(64),
|
|
@@ -9267,7 +9127,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
9267
9127
|
const readChunkLengths: number[] = [];
|
|
9268
9128
|
const typedCall = jest.fn((requestType: string, _responseType: string, request: any) => {
|
|
9269
9129
|
if (requestType === 'FilesystemPathInfoQuery') {
|
|
9270
|
-
return Promise.resolve({ message: { exist: true, directory: false, size:
|
|
9130
|
+
return Promise.resolve({ message: { exist: true, directory: false, size: 0x5f91 } });
|
|
9271
9131
|
}
|
|
9272
9132
|
if (requestType === 'FilesystemFileRead') {
|
|
9273
9133
|
readChunkLengths.push(request.chunk_len);
|