@onekeyfe/hd-core 1.2.0-alpha.109 → 1.2.0-alpha.110
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__/DeviceCommands.test.ts +2 -2
- package/__tests__/device-lifecycle-events.test.ts +1 -1
- package/__tests__/device-state-mapper.test.ts +4 -4
- package/__tests__/device-utils.test.ts +1 -1
- package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +54 -0
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +89 -36
- package/__tests__/get-device-state.test.ts +8 -8
- package/__tests__/protocol-v2-resources.test.ts +0 -8
- package/__tests__/protocol-v2.test.ts +236 -192
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +3 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
- package/dist/index.d.ts +20 -5
- package/dist/index.js +218 -134
- package/dist/protocols/protocol-v2/features.d.ts +4 -4
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/types/settings.d.ts +19 -4
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV3.ts +23 -16
- package/src/api/FirmwareUpdateV4.ts +174 -84
- package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +1 -1
- package/src/api/protocol-v2/DeviceInfoGet.ts +3 -3
- package/src/api/ton/TonSignMessage.ts +1 -1
- package/src/data/messages/messages-protocol-v2.json +14 -31
- package/src/device/DeviceStateMapper.ts +15 -15
- package/src/deviceProfile/buildDeviceFeatures.ts +3 -3
- package/src/protocols/protocol-v2/features.ts +6 -6
- package/src/protocols/protocol-v2/resources.ts +46 -9
- package/src/types/settings.ts +19 -4
|
@@ -28,6 +28,7 @@ import { DevicePool } from '../device/DevicePool';
|
|
|
28
28
|
import {
|
|
29
29
|
PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
30
30
|
ProtocolV2FirmwareTargetType,
|
|
31
|
+
isLegacyProtocolV2ProtocolInfo,
|
|
31
32
|
} from '../protocols/protocol-v2';
|
|
32
33
|
import { requestProtocolV2DeviceInfo } from '../protocols/protocol-v2/features';
|
|
33
34
|
import {
|
|
@@ -69,7 +70,6 @@ import type {
|
|
|
69
70
|
Features,
|
|
70
71
|
IFirmwareReleaseInfo,
|
|
71
72
|
IProtocolV2FirmwareComponent,
|
|
72
|
-
IProtocolV2ResourceManifestFile,
|
|
73
73
|
IVersionArray,
|
|
74
74
|
} from '../types';
|
|
75
75
|
import type { FirmwareByteSource } from './firmware/FirmwareArtifactSource';
|
|
@@ -89,6 +89,7 @@ const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 90 * 1000;
|
|
|
89
89
|
const PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT = 3 * 60 * 1000;
|
|
90
90
|
const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
|
|
91
91
|
const PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT = 15 * 1000;
|
|
92
|
+
const PROTOCOL_V2_START_UPDATE_TIMEOUT = 3 * 60 * 1000;
|
|
92
93
|
const PROTOCOL_V2_INSTALL_TIMEOUT = 8 * 60 * 1000;
|
|
93
94
|
const PROTOCOL_V2_MISSING_TARGET_STATUS_GRACE_TIMEOUT = 30 * 1000;
|
|
94
95
|
const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
|
|
@@ -112,13 +113,6 @@ const PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES = 1024 * 1024;
|
|
|
112
113
|
const PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT = 512;
|
|
113
114
|
const PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES = 256 * 1024 * 1024;
|
|
114
115
|
|
|
115
|
-
const getProtocolV2LocalResourceArchivePath = (entryName: string) => {
|
|
116
|
-
const match = entryName.match(
|
|
117
|
-
/(?:^|\/)((?:bundles\/|loaders\/(?:bootloader|rom)\/).+\.okpkg)$/iu
|
|
118
|
-
);
|
|
119
|
-
return match?.[1];
|
|
120
|
-
};
|
|
121
|
-
|
|
122
116
|
const PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS = new Set<FirmwareUpdateV4Target>(['se03', 'se04']);
|
|
123
117
|
|
|
124
118
|
const getProtocolV2ZipEntrySizes = (entry: JSZip.JSZipObject) => {
|
|
@@ -205,6 +199,8 @@ type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
|
205
199
|
path?: string;
|
|
206
200
|
};
|
|
207
201
|
|
|
202
|
+
type ProtocolV2FirmwareUpdateStartResponse = TypedResponseMessage<'Success'>;
|
|
203
|
+
|
|
208
204
|
type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId: number };
|
|
209
205
|
type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
|
|
210
206
|
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
@@ -381,6 +377,31 @@ const isProtocolV2ReconnectProbeError = (error: unknown) => {
|
|
|
381
377
|
);
|
|
382
378
|
};
|
|
383
379
|
|
|
380
|
+
const PROTOCOL_V2_BLE_INSTALL_INTERRUPTION_ERROR_CODES = new Set<number>([
|
|
381
|
+
HardwareErrorCode.BleConnectedError,
|
|
382
|
+
HardwareErrorCode.BleCharacteristicNotifyError,
|
|
383
|
+
HardwareErrorCode.BleForceCleanRunPromise,
|
|
384
|
+
HardwareErrorCode.BleDeviceDisconnected,
|
|
385
|
+
]);
|
|
386
|
+
|
|
387
|
+
const isProtocolV2BleInstallInterruptionError = (error: unknown) => {
|
|
388
|
+
if (
|
|
389
|
+
error instanceof HardwareError &&
|
|
390
|
+
PROTOCOL_V2_BLE_INSTALL_INTERRUPTION_ERROR_CODES.has(error.errorCode)
|
|
391
|
+
) {
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
396
|
+
const compactMessage = message.replace(/\s+/gu, '');
|
|
397
|
+
return (
|
|
398
|
+
/react native ble transport (?:released|disconnected)/u.test(message) ||
|
|
399
|
+
(compactMessage.includes('rxerrorerror6') &&
|
|
400
|
+
(compactMessage.includes('multiplatformbleadapter') ||
|
|
401
|
+
compactMessage.includes('multipalformebleadapter')))
|
|
402
|
+
);
|
|
403
|
+
};
|
|
404
|
+
|
|
384
405
|
const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
385
406
|
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
386
407
|
return (
|
|
@@ -390,6 +411,15 @@ const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
|
390
411
|
);
|
|
391
412
|
};
|
|
392
413
|
|
|
414
|
+
const isProtocolV2FirmwareUpdateEndpointUnavailable = (error: unknown) => {
|
|
415
|
+
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
416
|
+
return (
|
|
417
|
+
message.includes('handler not registered') ||
|
|
418
|
+
message.includes('message handler not found') ||
|
|
419
|
+
message.includes('unsupported message')
|
|
420
|
+
);
|
|
421
|
+
};
|
|
422
|
+
|
|
393
423
|
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
394
424
|
error instanceof HardwareError &&
|
|
395
425
|
(error.errorCode === HardwareErrorCode.FirmwareError ||
|
|
@@ -566,7 +596,7 @@ export const assertProtocolV2ReconnectIdentity = (
|
|
|
566
596
|
*
|
|
567
597
|
* It intentionally does not fall back to FirmwareUpdateV3/V1 behavior:
|
|
568
598
|
* - upload uses FilesystemFileWrite
|
|
569
|
-
* - install uses
|
|
599
|
+
* - install uses DeviceFirmwareUpdateRequest
|
|
570
600
|
* - completion waits for target status to finish, reboots to normal, then polls DeviceInfo
|
|
571
601
|
*/
|
|
572
602
|
export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareUpdateV4Params> {
|
|
@@ -584,6 +614,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
584
614
|
|
|
585
615
|
private protocolV2ExecutionInLoader = false;
|
|
586
616
|
|
|
617
|
+
private protocolV2LegacyDirectUpdate = false;
|
|
618
|
+
|
|
587
619
|
private protocolV2BootResourceStagingSafe = false;
|
|
588
620
|
|
|
589
621
|
private protocolV2CompletedTargetVersions = new Map<number, number>();
|
|
@@ -592,6 +624,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
592
624
|
|
|
593
625
|
private protocolV2FinalStatusVerified = false;
|
|
594
626
|
|
|
627
|
+
private protocolV2InstallAckReceived = false;
|
|
628
|
+
|
|
595
629
|
private protocolV2InstallBaselineVersions = new Map<number, string>();
|
|
596
630
|
|
|
597
631
|
private protocolV2LastRuntimeProbeFeatures?: Features;
|
|
@@ -1259,86 +1293,92 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1259
1293
|
);
|
|
1260
1294
|
}
|
|
1261
1295
|
const entries = zipEntries.filter(entry => !entry.dir);
|
|
1262
|
-
if (entries.length === 0) {
|
|
1296
|
+
if (entries.length === 0 || entries.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT + 1) {
|
|
1263
1297
|
throw ERRORS.TypedError(
|
|
1264
1298
|
HardwareErrorCode.RuntimeError,
|
|
1265
1299
|
'Protocol V2 local resource ZIP entry set is invalid',
|
|
1266
1300
|
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1267
1301
|
);
|
|
1268
1302
|
}
|
|
1269
|
-
|
|
1270
|
-
let
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1303
|
+
let declaredUncompressedSize = 0;
|
|
1304
|
+
let declaredCompressedSize = 0;
|
|
1305
|
+
for (const entry of entries) {
|
|
1306
|
+
const sizes = getProtocolV2ZipEntrySizes(entry);
|
|
1307
|
+
declaredCompressedSize += sizes.compressedSize;
|
|
1308
|
+
declaredUncompressedSize += sizes.uncompressedSize;
|
|
1309
|
+
const entryLimit =
|
|
1310
|
+
entry.name === 'manifest.json'
|
|
1311
|
+
? PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1312
|
+
: PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES;
|
|
1274
1313
|
if (
|
|
1275
|
-
|
|
1276
|
-
|
|
1314
|
+
sizes.uncompressedSize > entryLimit ||
|
|
1315
|
+
declaredCompressedSize > binary.byteLength ||
|
|
1316
|
+
declaredUncompressedSize >
|
|
1317
|
+
PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES + PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1277
1318
|
) {
|
|
1278
1319
|
throw ERRORS.TypedError(
|
|
1279
1320
|
HardwareErrorCode.RuntimeError,
|
|
1280
|
-
'Protocol V2 local resource
|
|
1321
|
+
'Protocol V2 local resource ZIP declared size exceeds the allowed limit',
|
|
1281
1322
|
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1282
1323
|
);
|
|
1283
1324
|
}
|
|
1284
|
-
manifestBinary = await manifestEntry.async('arraybuffer');
|
|
1285
|
-
if (
|
|
1286
|
-
manifestBinary.byteLength <= 0 ||
|
|
1287
|
-
manifestBinary.byteLength > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1288
|
-
) {
|
|
1289
|
-
throw ERRORS.TypedError(
|
|
1290
|
-
HardwareErrorCode.RuntimeError,
|
|
1291
|
-
'Protocol V2 local resource manifest size is invalid',
|
|
1292
|
-
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1293
|
-
);
|
|
1294
|
-
}
|
|
1295
|
-
let manifestValue: unknown;
|
|
1296
|
-
try {
|
|
1297
|
-
manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
|
|
1298
|
-
} catch (error) {
|
|
1299
|
-
throw ERRORS.TypedError(
|
|
1300
|
-
HardwareErrorCode.RuntimeError,
|
|
1301
|
-
`Protocol V2 local resource manifest is invalid: ${String(error)}`,
|
|
1302
|
-
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1303
|
-
);
|
|
1304
|
-
}
|
|
1305
|
-
selectedFiles = selectProtocolV2ResourceManifestFiles({
|
|
1306
|
-
manifest: parseProtocolV2ResourceManifest(manifestValue),
|
|
1307
|
-
targetsToUpdate: this.params.targetsToUpdate ?? [],
|
|
1308
|
-
});
|
|
1309
|
-
manifestDirectory = manifestEntry.name.slice(0, -'manifest.json'.length);
|
|
1310
|
-
} else {
|
|
1311
|
-
selectedFiles = entries.flatMap(entry => {
|
|
1312
|
-
const archivePath = getProtocolV2LocalResourceArchivePath(entry.name);
|
|
1313
|
-
if (!archivePath) return [];
|
|
1314
|
-
return [
|
|
1315
|
-
{
|
|
1316
|
-
archive_path: archivePath,
|
|
1317
|
-
original_name: archivePath.split('/').pop() ?? archivePath,
|
|
1318
|
-
device_path: `vol0:/${archivePath}`,
|
|
1319
|
-
size: getProtocolV2ZipEntrySizes(entry).uncompressedSize,
|
|
1320
|
-
sha256: '',
|
|
1321
|
-
},
|
|
1322
|
-
];
|
|
1323
|
-
});
|
|
1324
1325
|
}
|
|
1325
|
-
|
|
1326
|
+
const manifestEntry = zip.file('manifest.json');
|
|
1327
|
+
if (!manifestEntry) {
|
|
1328
|
+
throw ERRORS.TypedError(
|
|
1329
|
+
HardwareErrorCode.RuntimeError,
|
|
1330
|
+
'Protocol V2 local resource ZIP has no manifest.json',
|
|
1331
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
const manifestBinary = await manifestEntry.async('arraybuffer');
|
|
1335
|
+
if (
|
|
1336
|
+
manifestBinary.byteLength <= 0 ||
|
|
1337
|
+
manifestBinary.byteLength > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1338
|
+
) {
|
|
1339
|
+
throw ERRORS.TypedError(
|
|
1340
|
+
HardwareErrorCode.RuntimeError,
|
|
1341
|
+
'Protocol V2 local resource manifest size is invalid',
|
|
1342
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
let manifestValue: unknown;
|
|
1347
|
+
try {
|
|
1348
|
+
manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
throw ERRORS.TypedError(
|
|
1351
|
+
HardwareErrorCode.RuntimeError,
|
|
1352
|
+
`Protocol V2 local resource manifest is invalid: ${String(error)}`,
|
|
1353
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
const manifest = parseProtocolV2ResourceManifest(manifestValue);
|
|
1357
|
+
const selectedFiles = selectProtocolV2ResourceManifestFiles({
|
|
1358
|
+
manifest,
|
|
1359
|
+
targetsToUpdate: this.params.targetsToUpdate ?? [],
|
|
1360
|
+
});
|
|
1361
|
+
const expectedEntryNames = new Set([
|
|
1362
|
+
'manifest.json',
|
|
1363
|
+
...selectedFiles.map(file => file.archive_path),
|
|
1364
|
+
]);
|
|
1365
|
+
if (
|
|
1366
|
+
entries.length !== expectedEntryNames.size ||
|
|
1367
|
+
entries.some(entry => !expectedEntryNames.has(entry.name))
|
|
1368
|
+
) {
|
|
1326
1369
|
throw ERRORS.TypedError(
|
|
1327
1370
|
HardwareErrorCode.RuntimeError,
|
|
1328
|
-
'Protocol V2 local resource ZIP
|
|
1371
|
+
'Protocol V2 local resource ZIP contains an unexpected entry',
|
|
1329
1372
|
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1330
1373
|
);
|
|
1331
1374
|
}
|
|
1332
1375
|
|
|
1333
1376
|
let totalSize = 0;
|
|
1334
|
-
const materializedEntries: FirmwareMemoryArtifactEntry[] = [
|
|
1335
|
-
|
|
1377
|
+
const materializedEntries: FirmwareMemoryArtifactEntry[] = [
|
|
1378
|
+
{ entryName: 'manifest.json', binary: manifestBinary },
|
|
1379
|
+
];
|
|
1336
1380
|
for (const file of selectedFiles) {
|
|
1337
|
-
const entry =
|
|
1338
|
-
? zip.file(`${manifestDirectory}${file.archive_path}`)
|
|
1339
|
-
: entries.find(
|
|
1340
|
-
candidate => getProtocolV2LocalResourceArchivePath(candidate.name) === file.archive_path
|
|
1341
|
-
);
|
|
1381
|
+
const entry = zip.file(file.archive_path);
|
|
1342
1382
|
if (!entry) {
|
|
1343
1383
|
throw ERRORS.TypedError(
|
|
1344
1384
|
HardwareErrorCode.RuntimeError,
|
|
@@ -1357,21 +1397,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1357
1397
|
}
|
|
1358
1398
|
const fileBinary = await entry.async('arraybuffer');
|
|
1359
1399
|
const digest = bytesToHex(sha256(new Uint8Array(fileBinary)));
|
|
1360
|
-
if (
|
|
1361
|
-
fileBinary.byteLength !== file.size ||
|
|
1362
|
-
(file.sha256 && digest !== file.sha256.toLowerCase())
|
|
1363
|
-
) {
|
|
1400
|
+
if (fileBinary.byteLength !== file.size || digest !== file.sha256.toLowerCase()) {
|
|
1364
1401
|
throw ERRORS.TypedError(
|
|
1365
1402
|
HardwareErrorCode.RuntimeError,
|
|
1366
1403
|
`Protocol V2 local resource file does not match manifest: ${file.archive_path}`,
|
|
1367
1404
|
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1368
1405
|
);
|
|
1369
1406
|
}
|
|
1370
|
-
normalizedFiles.push({ ...file, sha256: digest });
|
|
1371
1407
|
materializedEntries.push({ entryName: file.archive_path, binary: fileBinary });
|
|
1372
1408
|
}
|
|
1373
|
-
manifestBinary ??= new TextEncoder().encode(JSON.stringify({ files: normalizedFiles })).buffer;
|
|
1374
|
-
materializedEntries.unshift({ entryName: 'manifest.json', binary: manifestBinary });
|
|
1375
1409
|
return { binary, materializedEntries };
|
|
1376
1410
|
}
|
|
1377
1411
|
|
|
@@ -2134,6 +2168,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2134
2168
|
return this.device.features?.mode === 'romloader';
|
|
2135
2169
|
}
|
|
2136
2170
|
|
|
2171
|
+
private isLegacyProtocolV2Runtime() {
|
|
2172
|
+
const protocolInfo = this.device.state?.raw?.protocolV2ProtocolInfo;
|
|
2173
|
+
return protocolInfo ? isLegacyProtocolV2ProtocolInfo(protocolInfo) : false;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2137
2176
|
private async rebootProtocolV2ToBootloader() {
|
|
2138
2177
|
try {
|
|
2139
2178
|
this.postTipMessage(FirmwareUpdateTipMessage.AutoRebootToBootloader);
|
|
@@ -2153,15 +2192,18 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2153
2192
|
}
|
|
2154
2193
|
|
|
2155
2194
|
async enterProtocolV2BootloaderMode() {
|
|
2195
|
+
this.protocolV2LegacyDirectUpdate = false;
|
|
2156
2196
|
// romloader is the first update environment and forwards targets to bootloader.
|
|
2157
2197
|
// It rejects DeviceRebootType.Bootloader, so reuse the current connection.
|
|
2158
2198
|
if (this.isProtocolV2RomloaderMode()) {
|
|
2159
2199
|
Log.debug('Protocol V2 device is in romloader mode; start firmware update directly');
|
|
2200
|
+
this.protocolV2LegacyDirectUpdate = this.isLegacyProtocolV2Runtime();
|
|
2160
2201
|
this.protocolV2ExecutionInLoader = true;
|
|
2161
2202
|
return false;
|
|
2162
2203
|
}
|
|
2163
2204
|
if (this.isProtocolV2BootloaderMode()) {
|
|
2164
2205
|
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot');
|
|
2206
|
+
this.protocolV2LegacyDirectUpdate = this.isLegacyProtocolV2Runtime();
|
|
2165
2207
|
this.protocolV2ExecutionInLoader = true;
|
|
2166
2208
|
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
2167
2209
|
return false;
|
|
@@ -2672,7 +2714,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2672
2714
|
let missingTargetStatusSince: number | undefined;
|
|
2673
2715
|
let missingTargetStatusKey: string | undefined;
|
|
2674
2716
|
let normalModeWithoutInstallEvidenceSince: number | undefined;
|
|
2675
|
-
let installEvidenceObserved =
|
|
2717
|
+
let installEvidenceObserved = this.protocolV2InstallAckReceived;
|
|
2676
2718
|
const resetMissingTargetStatusGrace = () => {
|
|
2677
2719
|
missingTargetStatusSince = undefined;
|
|
2678
2720
|
missingTargetStatusKey = undefined;
|
|
@@ -2692,7 +2734,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2692
2734
|
try {
|
|
2693
2735
|
const statusResponse = await this.device.getCommands().typedCall(
|
|
2694
2736
|
'DeviceFirmwareUpdateStatusGet',
|
|
2695
|
-
|
|
2737
|
+
'DeviceFirmwareUpdateStatus',
|
|
2696
2738
|
{
|
|
2697
2739
|
fields: {
|
|
2698
2740
|
status: true,
|
|
@@ -2702,11 +2744,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2702
2744
|
},
|
|
2703
2745
|
{ timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT }
|
|
2704
2746
|
);
|
|
2705
|
-
if (statusResponse.type === 'Success') {
|
|
2706
|
-
this.protocolV2FinalStatusVerified = true;
|
|
2707
|
-
this.postProgressMessage(100, 'installingFirmware');
|
|
2708
|
-
return;
|
|
2709
|
-
}
|
|
2710
2747
|
const statusTargets = (statusResponse.message.records ??
|
|
2711
2748
|
[]) as ProtocolV2FirmwareUpdateStatusTarget[];
|
|
2712
2749
|
if (
|
|
@@ -3111,12 +3148,65 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
3111
3148
|
}: {
|
|
3112
3149
|
targets: Array<{ target_id: number; path: string }>;
|
|
3113
3150
|
}) {
|
|
3151
|
+
this.protocolV2InstallAckReceived = false;
|
|
3114
3152
|
this.protocolV2LastRuntimeProbeFeatures = undefined;
|
|
3115
|
-
const
|
|
3116
|
-
|
|
3117
|
-
|
|
3153
|
+
const startUpdate = () =>
|
|
3154
|
+
this.device
|
|
3155
|
+
.getCommands()
|
|
3156
|
+
.typedCall(
|
|
3157
|
+
'DeviceFirmwareUpdateRequest',
|
|
3158
|
+
'Success',
|
|
3159
|
+
{ targets },
|
|
3160
|
+
{ timeoutMs: PROTOCOL_V2_START_UPDATE_TIMEOUT }
|
|
3161
|
+
);
|
|
3162
|
+
let response: ProtocolV2FirmwareUpdateStartResponse | undefined;
|
|
3163
|
+
try {
|
|
3164
|
+
response = await startUpdate();
|
|
3165
|
+
this.protocolV2InstallAckReceived = true;
|
|
3166
|
+
} catch (error) {
|
|
3167
|
+
if (this.isBleReconnect() && isProtocolV2BleInstallInterruptionError(error)) {
|
|
3168
|
+
this.throwIfAborted();
|
|
3169
|
+
// Installation can reboot the device before the Success response reaches
|
|
3170
|
+
// the host. The request has side effects, so do not replay it; reconnect
|
|
3171
|
+
// and let status polling determine whether installation was accepted.
|
|
3172
|
+
Log.log(
|
|
3173
|
+
'[FirmwareUpdateV4] install request interrupted by device reboot; continue status polling: ',
|
|
3174
|
+
error
|
|
3175
|
+
);
|
|
3176
|
+
} else if (
|
|
3177
|
+
this.protocolV2LegacyDirectUpdate &&
|
|
3178
|
+
isProtocolV2FirmwareUpdateEndpointUnavailable(error)
|
|
3179
|
+
) {
|
|
3180
|
+
// Both legacy loaders register this request. A missing handler therefore identifies
|
|
3181
|
+
// a legacy App before dispatch, so it is safe to reboot and retry the unhandled request.
|
|
3182
|
+
this.protocolV2LegacyDirectUpdate = false;
|
|
3183
|
+
Log.debug(
|
|
3184
|
+
'[FirmwareUpdateV4] legacy App does not expose DeviceFirmwareUpdateRequest; rebooting to bootloader'
|
|
3185
|
+
);
|
|
3186
|
+
await this.rebootProtocolV2ToBootloader();
|
|
3187
|
+
try {
|
|
3188
|
+
response = await startUpdate();
|
|
3189
|
+
this.protocolV2InstallAckReceived = true;
|
|
3190
|
+
} catch (retryError) {
|
|
3191
|
+
if (!(this.isBleReconnect() && isProtocolV2BleInstallInterruptionError(retryError))) {
|
|
3192
|
+
throw retryError;
|
|
3193
|
+
}
|
|
3194
|
+
this.throwIfAborted();
|
|
3195
|
+
Log.log(
|
|
3196
|
+
'[FirmwareUpdateV4] install request interrupted after legacy reboot; continue status polling: ',
|
|
3197
|
+
retryError
|
|
3198
|
+
);
|
|
3199
|
+
}
|
|
3200
|
+
} else {
|
|
3201
|
+
throw error;
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
this.protocolV2LegacyDirectUpdate = false;
|
|
3205
|
+
// A Success ACK or an install-time disconnect both end confirmation. In the
|
|
3206
|
+
// latter case only status polling may establish the final outcome.
|
|
3118
3207
|
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdating);
|
|
3119
3208
|
this.postProgressMessage(0, 'installingFirmware');
|
|
3209
|
+
return response;
|
|
3120
3210
|
}
|
|
3121
3211
|
|
|
3122
3212
|
private async protocolV2Reboot(rebootType: DeviceRebootType) {
|
|
@@ -25,7 +25,7 @@ export default class DeviceFactoryInfoSet extends BaseMethod<DeviceFactoryInfoSe
|
|
|
25
25
|
info: {
|
|
26
26
|
version: this.params.version,
|
|
27
27
|
serial_number: this.params.serial_number,
|
|
28
|
-
|
|
28
|
+
burn_in_completed: this.params.burn_in_completed,
|
|
29
29
|
factory_test_completed: this.params.factory_test_completed,
|
|
30
30
|
manufacture_time: this.params.manufacture_time,
|
|
31
31
|
},
|
|
@@ -5,7 +5,7 @@ import { invalidParameter } from '../helpers/filesystemValidation';
|
|
|
5
5
|
|
|
6
6
|
export type DeviceInfoGetTargets = {
|
|
7
7
|
hw?: boolean;
|
|
8
|
-
|
|
8
|
+
fw?: boolean;
|
|
9
9
|
coprocessor?: boolean;
|
|
10
10
|
se1?: boolean;
|
|
11
11
|
se2?: boolean;
|
|
@@ -27,7 +27,7 @@ export type DeviceInfoGetParams = {
|
|
|
27
27
|
|
|
28
28
|
const TARGET_KEYS: (keyof DeviceInfoGetTargets)[] = [
|
|
29
29
|
'hw',
|
|
30
|
-
'
|
|
30
|
+
'fw',
|
|
31
31
|
'coprocessor',
|
|
32
32
|
'se1',
|
|
33
33
|
'se2',
|
|
@@ -39,7 +39,7 @@ const TYPE_KEYS: (keyof DeviceInfoGetTypes)[] = ['version', 'build_id', 'hash',
|
|
|
39
39
|
|
|
40
40
|
const DEFAULT_TARGETS: DeviceInfoGetTargets = {
|
|
41
41
|
hw: true,
|
|
42
|
-
|
|
42
|
+
fw: true,
|
|
43
43
|
coprocessor: true,
|
|
44
44
|
};
|
|
45
45
|
|
|
@@ -174,7 +174,7 @@ export default class TonSignMessage extends BaseMethod<HardwareTonSignMessage> {
|
|
|
174
174
|
if (!request.init_data_length) {
|
|
175
175
|
const deviceType = this.device.getCurrentDeviceType();
|
|
176
176
|
const hasClassic = DeviceModelToTypes.model_classic1s.includes(deviceType);
|
|
177
|
-
const signingMessage = request.signning_message;
|
|
177
|
+
const signingMessage = request.signing_message ?? request.signning_message;
|
|
178
178
|
// Blind signing omits the signing message in both protocol schemas.
|
|
179
179
|
const shouldSkipValidation = signingMessage == null;
|
|
180
180
|
|
|
@@ -462,10 +462,9 @@
|
|
|
462
462
|
"MessageType_FilesystemDirMake": 60809,
|
|
463
463
|
"MessageType_FilesystemDirRemove": 60810,
|
|
464
464
|
"MessageType_FilesystemFormat": 60811,
|
|
465
|
-
"
|
|
466
|
-
"
|
|
467
|
-
"
|
|
468
|
-
"MessageType_DeviceFirmwareUpdateStatus": 61003,
|
|
465
|
+
"MessageType_DeviceFirmwareUpdateRequest": 61000,
|
|
466
|
+
"MessageType_DeviceFirmwareUpdateStatusGet": 61001,
|
|
467
|
+
"MessageType_DeviceFirmwareUpdateStatus": 61002,
|
|
469
468
|
"MessageType_DeviceSessionGet": 61200,
|
|
470
469
|
"MessageType_DeviceSession": 61201,
|
|
471
470
|
"MessageType_DeviceSessionAskPin": 61202,
|
|
@@ -10925,7 +10924,7 @@
|
|
|
10925
10924
|
"type": "bytes",
|
|
10926
10925
|
"id": 1
|
|
10927
10926
|
},
|
|
10928
|
-
"
|
|
10927
|
+
"signing_message": {
|
|
10929
10928
|
"type": "bytes",
|
|
10930
10929
|
"id": 2
|
|
10931
10930
|
},
|
|
@@ -11814,13 +11813,13 @@
|
|
|
11814
11813
|
"type": "string",
|
|
11815
11814
|
"id": 2
|
|
11816
11815
|
},
|
|
11817
|
-
"
|
|
11816
|
+
"burn_in_completed": {
|
|
11818
11817
|
"type": "bool",
|
|
11819
|
-
"id":
|
|
11818
|
+
"id": 3
|
|
11820
11819
|
},
|
|
11821
|
-
"
|
|
11820
|
+
"factory_test_completed": {
|
|
11822
11821
|
"type": "bool",
|
|
11823
|
-
"id":
|
|
11822
|
+
"id": 4
|
|
11824
11823
|
},
|
|
11825
11824
|
"manufacture_time": {
|
|
11826
11825
|
"type": "DeviceFactoryInfoManufactureTime",
|
|
@@ -11907,7 +11906,7 @@
|
|
|
11907
11906
|
}
|
|
11908
11907
|
}
|
|
11909
11908
|
},
|
|
11910
|
-
"
|
|
11909
|
+
"DeviceFirmwareUpdateRequest": {
|
|
11911
11910
|
"fields": {
|
|
11912
11911
|
"targets": {
|
|
11913
11912
|
"rule": "repeated",
|
|
@@ -11916,9 +11915,6 @@
|
|
|
11916
11915
|
}
|
|
11917
11916
|
}
|
|
11918
11917
|
},
|
|
11919
|
-
"DeviceFirmwareUpdateRequest": {
|
|
11920
|
-
"fields": {}
|
|
11921
|
-
},
|
|
11922
11918
|
"DeviceFirmwareUpdateRecord": {
|
|
11923
11919
|
"fields": {
|
|
11924
11920
|
"target_id": {
|
|
@@ -12100,7 +12096,7 @@
|
|
|
12100
12096
|
"type": "bool",
|
|
12101
12097
|
"id": 100
|
|
12102
12098
|
},
|
|
12103
|
-
"
|
|
12099
|
+
"fw": {
|
|
12104
12100
|
"type": "bool",
|
|
12105
12101
|
"id": 200
|
|
12106
12102
|
},
|
|
@@ -12169,7 +12165,7 @@
|
|
|
12169
12165
|
"type": "DeviceHardwareInfo",
|
|
12170
12166
|
"id": 100
|
|
12171
12167
|
},
|
|
12172
|
-
"
|
|
12168
|
+
"fw": {
|
|
12173
12169
|
"type": "DeviceMainMcuInfo",
|
|
12174
12170
|
"id": 200
|
|
12175
12171
|
},
|
|
@@ -12707,12 +12703,9 @@
|
|
|
12707
12703
|
"id": 1
|
|
12708
12704
|
},
|
|
12709
12705
|
"text": {
|
|
12706
|
+
"rule": "required",
|
|
12710
12707
|
"type": "string",
|
|
12711
12708
|
"id": 2
|
|
12712
|
-
},
|
|
12713
|
-
"text_id": {
|
|
12714
|
-
"type": "uint32",
|
|
12715
|
-
"id": 3
|
|
12716
12709
|
}
|
|
12717
12710
|
}
|
|
12718
12711
|
},
|
|
@@ -12743,6 +12736,7 @@
|
|
|
12743
12736
|
"ViewSignPage": {
|
|
12744
12737
|
"fields": {
|
|
12745
12738
|
"title": {
|
|
12739
|
+
"rule": "required",
|
|
12746
12740
|
"type": "string",
|
|
12747
12741
|
"id": 1
|
|
12748
12742
|
},
|
|
@@ -12776,16 +12770,13 @@
|
|
|
12776
12770
|
"options": {
|
|
12777
12771
|
"default": "LayoutDefault"
|
|
12778
12772
|
}
|
|
12779
|
-
},
|
|
12780
|
-
"title_id": {
|
|
12781
|
-
"type": "uint32",
|
|
12782
|
-
"id": 8
|
|
12783
12773
|
}
|
|
12784
12774
|
}
|
|
12785
12775
|
},
|
|
12786
12776
|
"ViewVerifyPage": {
|
|
12787
12777
|
"fields": {
|
|
12788
12778
|
"title": {
|
|
12779
|
+
"rule": "required",
|
|
12789
12780
|
"type": "string",
|
|
12790
12781
|
"id": 1
|
|
12791
12782
|
},
|
|
@@ -12810,14 +12801,6 @@
|
|
|
12810
12801
|
"value_key": {
|
|
12811
12802
|
"type": "uint32",
|
|
12812
12803
|
"id": 6
|
|
12813
|
-
},
|
|
12814
|
-
"title_id": {
|
|
12815
|
-
"type": "uint32",
|
|
12816
|
-
"id": 7
|
|
12817
|
-
},
|
|
12818
|
-
"chain_id": {
|
|
12819
|
-
"type": "uint32",
|
|
12820
|
-
"id": 8
|
|
12821
12804
|
}
|
|
12822
12805
|
}
|
|
12823
12806
|
},
|
|
@@ -267,11 +267,11 @@ export const mapProtocolV2DeviceInfoToState = (
|
|
|
267
267
|
}
|
|
268
268
|
: { mode },
|
|
269
269
|
versions: definedEntries({
|
|
270
|
-
firmware: imageVersion(info.
|
|
271
|
-
applicationP1: imageVersion(info.
|
|
272
|
-
applicationP2: imageVersion(info.
|
|
273
|
-
bootloader: imageVersion(info.
|
|
274
|
-
board: imageVersion(info.
|
|
270
|
+
firmware: imageVersion(info.fw?.application),
|
|
271
|
+
applicationP1: imageVersion(info.fw?.application),
|
|
272
|
+
applicationP2: imageVersion(info.fw?.application_data),
|
|
273
|
+
bootloader: imageVersion(info.fw?.bootloader),
|
|
274
|
+
board: imageVersion(info.fw?.romloader),
|
|
275
275
|
ble: imageVersion(info.coprocessor?.application),
|
|
276
276
|
se01: imageVersion(info.se1?.application),
|
|
277
277
|
se02: imageVersion(info.se2?.application),
|
|
@@ -283,16 +283,16 @@ export const mapProtocolV2DeviceInfoToState = (
|
|
|
283
283
|
se04Boot: imageVersion(info.se4?.bootloader),
|
|
284
284
|
}),
|
|
285
285
|
verification: definedEntries({
|
|
286
|
-
firmwareBuildId: imageBuildId(info.
|
|
287
|
-
firmwareHash: imageHash(info.
|
|
288
|
-
applicationP1BuildId: imageBuildId(info.
|
|
289
|
-
applicationP1Hash: imageHash(info.
|
|
290
|
-
applicationP2BuildId: imageBuildId(info.
|
|
291
|
-
applicationP2Hash: imageHash(info.
|
|
292
|
-
bootloaderBuildId: imageBuildId(info.
|
|
293
|
-
bootloaderHash: imageHash(info.
|
|
294
|
-
boardBuildId: imageBuildId(info.
|
|
295
|
-
boardHash: imageHash(info.
|
|
286
|
+
firmwareBuildId: imageBuildId(info.fw?.application),
|
|
287
|
+
firmwareHash: imageHash(info.fw?.application),
|
|
288
|
+
applicationP1BuildId: imageBuildId(info.fw?.application),
|
|
289
|
+
applicationP1Hash: imageHash(info.fw?.application),
|
|
290
|
+
applicationP2BuildId: imageBuildId(info.fw?.application_data),
|
|
291
|
+
applicationP2Hash: imageHash(info.fw?.application_data),
|
|
292
|
+
bootloaderBuildId: imageBuildId(info.fw?.bootloader),
|
|
293
|
+
bootloaderHash: imageHash(info.fw?.bootloader),
|
|
294
|
+
boardBuildId: imageBuildId(info.fw?.romloader),
|
|
295
|
+
boardHash: imageHash(info.fw?.romloader),
|
|
296
296
|
bleBuildId: imageBuildId(info.coprocessor?.application),
|
|
297
297
|
bleHash: imageHash(info.coprocessor?.application),
|
|
298
298
|
se01BuildId: imageBuildId(info.se1?.application),
|
|
@@ -216,9 +216,9 @@ export const buildProtocolV2FeaturesPayload = ({
|
|
|
216
216
|
runtimeMode?: ProtocolV2RuntimeMode;
|
|
217
217
|
}): Features => {
|
|
218
218
|
const info = deviceInfo;
|
|
219
|
-
const fwApplication = info?.
|
|
220
|
-
const fwBootloader = info?.
|
|
221
|
-
const fwBoard = info?.
|
|
219
|
+
const fwApplication = info?.fw?.application;
|
|
220
|
+
const fwBootloader = info?.fw?.bootloader;
|
|
221
|
+
const fwBoard = info?.fw?.romloader;
|
|
222
222
|
const bleApplication = info?.coprocessor?.application;
|
|
223
223
|
const status = deviceStatus;
|
|
224
224
|
const incomingSerialNo = info?.hw?.serial_no;
|