@onekeyfe/hd-core 1.2.0-alpha.183 → 1.2.0-alpha.185
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__/AllNetworkGetAddressBase.tracing.test.ts +20 -4
- package/__tests__/DeviceCommands.test.ts +13 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -2
- package/__tests__/check-firmware-release-protocol-v2.test.ts +0 -2
- package/__tests__/device-lifecycle-events.test.ts +19 -0
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +102 -2
- package/__tests__/open-wallet-session.test.ts +127 -1
- package/__tests__/protocol-v2-resources.test.ts +0 -4
- package/__tests__/protocol-v2-unlock-policy.test.ts +35 -1
- package/__tests__/protocol-v2.test.ts +155 -2
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +2 -2
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +1 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +3 -0
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.js +644 -347
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +2 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +3 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/settings.d.ts +2 -2
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +3 -0
- package/dist/utils/deviceFeaturesUtils.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/FirmwareUpdateV4.ts +193 -21
- package/src/api/OpenWalletSession.ts +39 -9
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +2 -1
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +9 -3
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +7 -1
- package/src/core/index.ts +16 -2
- package/src/data/messages/messages-protocol-v2.json +111 -1
- package/src/device/Device.ts +3 -1
- package/src/events/ui-request.ts +3 -0
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +8 -1
- package/src/protocols/protocol-v2/walletSession.ts +65 -22
- package/src/types/settings.ts +4 -2
- package/src/utils/deviceFeaturesUtils.ts +4 -0
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
EDeviceType,
|
|
3
|
+
ERRORS,
|
|
4
|
+
HardwareError,
|
|
5
|
+
HardwareErrorCode,
|
|
6
|
+
isBleStaleBondHardwareError,
|
|
7
|
+
wait,
|
|
8
|
+
} from '@onekeyfe/hd-shared';
|
|
2
9
|
import JSZip from 'jszip';
|
|
3
10
|
import {
|
|
4
11
|
DeviceRebootType,
|
|
@@ -44,6 +51,7 @@ import {
|
|
|
44
51
|
getProtocolV2UnknownErrorText,
|
|
45
52
|
isProtocolV2DeviceDisconnectedError,
|
|
46
53
|
} from './protocol-v2/helpers';
|
|
54
|
+
import { isProtocolV2ResponseTimeout } from './helpers/protocolV2FileWrite';
|
|
47
55
|
import {
|
|
48
56
|
openFirmwareByteSource,
|
|
49
57
|
readFirmwareByteSourceFully,
|
|
@@ -55,6 +63,7 @@ import {
|
|
|
55
63
|
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
56
64
|
validateFirmwareUpdatePreparedPlan,
|
|
57
65
|
} from './firmware/FirmwareUpdatePreparedPlan';
|
|
66
|
+
|
|
58
67
|
import type {
|
|
59
68
|
FirmwareArtifactReference,
|
|
60
69
|
FirmwareUpdateV4Params,
|
|
@@ -84,6 +93,7 @@ const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
|
|
|
84
93
|
const PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT = 15 * 1000;
|
|
85
94
|
const PROTOCOL_V2_INSTALL_TIMEOUT = 5 * 60 * 1000;
|
|
86
95
|
const PROTOCOL_V2_INSTALL_STATUS_INITIAL_DELAY = 1000;
|
|
96
|
+
const PROTOCOL_V2_INSTALL_FINISHED_AFTER_RECONNECT_POLLS = 4;
|
|
87
97
|
const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
|
|
88
98
|
const PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS = 1;
|
|
89
99
|
const PROTOCOL_V2_TARGET_STATUS_FINISHED = 2;
|
|
@@ -182,6 +192,11 @@ const getProtocolV2DeviceTransferProgress = (
|
|
|
182
192
|
type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
183
193
|
target_id: number | string;
|
|
184
194
|
status?: number | string;
|
|
195
|
+
progress_percent?: number;
|
|
196
|
+
phase_info?: {
|
|
197
|
+
phase: number | string;
|
|
198
|
+
progress_percent: number;
|
|
199
|
+
};
|
|
185
200
|
payload_version?: number;
|
|
186
201
|
path?: string;
|
|
187
202
|
};
|
|
@@ -360,11 +375,12 @@ const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
|
360
375
|
};
|
|
361
376
|
|
|
362
377
|
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
363
|
-
error
|
|
364
|
-
(error
|
|
365
|
-
error.errorCode === HardwareErrorCode.
|
|
366
|
-
|
|
367
|
-
|
|
378
|
+
isBleStaleBondHardwareError(error) ||
|
|
379
|
+
(error instanceof HardwareError &&
|
|
380
|
+
(error.errorCode === HardwareErrorCode.ActionCancelled ||
|
|
381
|
+
error.errorCode === HardwareErrorCode.FirmwareError ||
|
|
382
|
+
error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
|
|
383
|
+
error.params?.firmwareUpdateCode === PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE));
|
|
368
384
|
|
|
369
385
|
const isProtocolV2TargetStatusFinished = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) =>
|
|
370
386
|
normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_FINISHED;
|
|
@@ -401,6 +417,18 @@ const normalizeProtocolV2TargetStatus = (
|
|
|
401
417
|
return undefined;
|
|
402
418
|
};
|
|
403
419
|
|
|
420
|
+
const PROTOCOL_V2_INSTALL_PHASE_BY_DECODED_VALUE = new Map<
|
|
421
|
+
number | string,
|
|
422
|
+
'prepare' | 'install' | 'verify'
|
|
423
|
+
>([
|
|
424
|
+
[0, 'prepare'],
|
|
425
|
+
[1, 'install'],
|
|
426
|
+
[2, 'verify'],
|
|
427
|
+
['FW_MGMT_UPDATER_PHASE_PREPARE', 'prepare'],
|
|
428
|
+
['FW_MGMT_UPDATER_PHASE_INSTALL', 'install'],
|
|
429
|
+
['FW_MGMT_UPDATER_PHASE_VERIFY', 'verify'],
|
|
430
|
+
]);
|
|
431
|
+
|
|
404
432
|
const normalizeProtocolV2Hex = (value?: string) => value?.replace(/^0x/i, '').toLowerCase();
|
|
405
433
|
|
|
406
434
|
const versionArrayToNumber = (version?: IVersionArray) => {
|
|
@@ -458,6 +486,11 @@ const toProtocolV2FiniteNumber = (value: unknown) => {
|
|
|
458
486
|
return undefined;
|
|
459
487
|
};
|
|
460
488
|
|
|
489
|
+
const normalizeProtocolV2ProgressPercent = (value: unknown) => {
|
|
490
|
+
const progress = toProtocolV2FiniteNumber(value);
|
|
491
|
+
return progress === undefined ? undefined : Math.min(100, Math.max(0, progress));
|
|
492
|
+
};
|
|
493
|
+
|
|
461
494
|
export const isProtocolV2FirmwareFingerprintValid = (
|
|
462
495
|
binary: ArrayBuffer | Uint8Array,
|
|
463
496
|
fingerprint: string | undefined
|
|
@@ -1812,6 +1845,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1812
1845
|
if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash) return false;
|
|
1813
1846
|
return true;
|
|
1814
1847
|
} catch (error) {
|
|
1848
|
+
if (isBleStaleBondHardwareError(error)) {
|
|
1849
|
+
throw error;
|
|
1850
|
+
}
|
|
1815
1851
|
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
1816
1852
|
return false;
|
|
1817
1853
|
}
|
|
@@ -1905,7 +1941,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1905
1941
|
}
|
|
1906
1942
|
lastError = new Error('Protocol V2 device is reachable but is not in bootloader mode');
|
|
1907
1943
|
} catch (error) {
|
|
1908
|
-
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
1944
|
+
if (isBleStaleBondHardwareError(error) || this.isProtocolV2ReconnectIdentityError(error)) {
|
|
1909
1945
|
throw error;
|
|
1910
1946
|
}
|
|
1911
1947
|
shouldReconnect = true;
|
|
@@ -2187,6 +2223,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2187
2223
|
});
|
|
2188
2224
|
return processedSize + source.size;
|
|
2189
2225
|
} catch (error) {
|
|
2226
|
+
if (isBleStaleBondHardwareError(error)) {
|
|
2227
|
+
throw error;
|
|
2228
|
+
}
|
|
2190
2229
|
lastError = error;
|
|
2191
2230
|
if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
2192
2231
|
await this.recoverProtocolV2FileTransfer();
|
|
@@ -2296,6 +2335,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2296
2335
|
for (const target of matchingTargets) {
|
|
2297
2336
|
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2298
2337
|
const pathConflictsWithCurrentInstall =
|
|
2338
|
+
targetId !== undefined &&
|
|
2299
2339
|
Boolean(target.path) &&
|
|
2300
2340
|
Boolean(resolvedExpectedPaths.get(targetId)) &&
|
|
2301
2341
|
target.path !== resolvedExpectedPaths.get(targetId) &&
|
|
@@ -2359,16 +2399,54 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2359
2399
|
}
|
|
2360
2400
|
|
|
2361
2401
|
if (expectedTargetIds.size > 0 && matchingTargets.length > 0) {
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2402
|
+
let detailedProgress = completedTargetIds.size * 100;
|
|
2403
|
+
let installProgressMetadata:
|
|
2404
|
+
| {
|
|
2405
|
+
installTargetId: number;
|
|
2406
|
+
installPhase: 'prepare' | 'install' | 'verify';
|
|
2407
|
+
installPhaseProgress: number;
|
|
2408
|
+
}
|
|
2409
|
+
| undefined;
|
|
2410
|
+
let hasInProgressTarget = false;
|
|
2411
|
+
matchingTargets.forEach(target => {
|
|
2412
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2413
|
+
if (
|
|
2414
|
+
targetId === undefined ||
|
|
2415
|
+
completedTargetIds.has(targetId) ||
|
|
2416
|
+
!isProtocolV2TargetStatusInProgress(target.status) ||
|
|
2417
|
+
(liveTargetIds && !liveTargetIds.has(targetId))
|
|
2418
|
+
) {
|
|
2419
|
+
return;
|
|
2420
|
+
}
|
|
2421
|
+
hasInProgressTarget = true;
|
|
2422
|
+
detailedProgress += normalizeProtocolV2ProgressPercent(target.progress_percent) ?? 0;
|
|
2423
|
+
const installPhase = target.phase_info
|
|
2424
|
+
? PROTOCOL_V2_INSTALL_PHASE_BY_DECODED_VALUE.get(target.phase_info.phase)
|
|
2425
|
+
: undefined;
|
|
2426
|
+
const installPhaseProgress = normalizeProtocolV2ProgressPercent(
|
|
2427
|
+
target.phase_info?.progress_percent
|
|
2428
|
+
);
|
|
2429
|
+
if (installPhase && installPhaseProgress !== undefined && !installProgressMetadata) {
|
|
2430
|
+
installProgressMetadata = {
|
|
2431
|
+
installTargetId: targetId,
|
|
2432
|
+
installPhase,
|
|
2433
|
+
installPhaseProgress,
|
|
2434
|
+
};
|
|
2435
|
+
}
|
|
2436
|
+
});
|
|
2365
2437
|
const completedProgress = Math.floor(
|
|
2366
2438
|
(completedTargetIds.size / expectedTargetIds.size) * 100
|
|
2367
2439
|
);
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2440
|
+
const reportedProgress = Math.floor(detailedProgress / expectedTargetIds.size);
|
|
2441
|
+
const progress = Math.min(
|
|
2442
|
+
99,
|
|
2443
|
+
Math.max(completedProgress, reportedProgress, hasInProgressTarget ? 1 : 0)
|
|
2444
|
+
);
|
|
2445
|
+
if (installProgressMetadata) {
|
|
2446
|
+
this.postProgressMessage(progress, 'installingFirmware', installProgressMetadata);
|
|
2447
|
+
} else {
|
|
2448
|
+
this.postProgressMessage(progress, 'installingFirmware');
|
|
2449
|
+
}
|
|
2372
2450
|
}
|
|
2373
2451
|
|
|
2374
2452
|
return false;
|
|
@@ -2444,9 +2522,14 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2444
2522
|
// The loader may release either USB or BLE as installation starts. Recovery reconnects
|
|
2445
2523
|
// directly to status polling without generic Ping or DeviceInfo probes.
|
|
2446
2524
|
let shouldReconnect = this.protocolV2InstallNeedsReconnect;
|
|
2525
|
+
let installTransportReleaseObserved = shouldReconnect;
|
|
2447
2526
|
this.protocolV2InstallNeedsReconnect = false;
|
|
2448
2527
|
let deviceInfo: ProtocolV2DeviceInfo | undefined;
|
|
2449
2528
|
let bleInstallLinkReady = false;
|
|
2529
|
+
let installReconnectObserved = false;
|
|
2530
|
+
let finishedStatusSnapshotKey: string | undefined;
|
|
2531
|
+
let finishedStatusSnapshotPolls = 0;
|
|
2532
|
+
let fastFinishedInstallProgressReported = false;
|
|
2450
2533
|
let installEvidenceObserved = this.protocolV2InstallTerminalSuccessObserved;
|
|
2451
2534
|
let currentInstallStatusObserved = false;
|
|
2452
2535
|
const liveTargetIds = new Set<number>();
|
|
@@ -2463,6 +2546,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2463
2546
|
deviceInfo = isBleInstallReconnect
|
|
2464
2547
|
? undefined
|
|
2465
2548
|
: await this.verifyProtocolV2ReconnectIdentity();
|
|
2549
|
+
if (installTransportReleaseObserved) {
|
|
2550
|
+
installReconnectObserved = true;
|
|
2551
|
+
}
|
|
2466
2552
|
shouldReconnect = false;
|
|
2467
2553
|
}
|
|
2468
2554
|
let currentDeviceInfo = deviceInfo;
|
|
@@ -2473,6 +2559,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2473
2559
|
{
|
|
2474
2560
|
fields: {
|
|
2475
2561
|
status: true,
|
|
2562
|
+
progress_percent: true,
|
|
2563
|
+
phase_info: true,
|
|
2476
2564
|
payload_version: true,
|
|
2477
2565
|
path: true,
|
|
2478
2566
|
},
|
|
@@ -2502,8 +2590,73 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2502
2590
|
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2503
2591
|
return targetId !== undefined && expectedTargetIds.has(targetId);
|
|
2504
2592
|
});
|
|
2593
|
+
const matchingFinishedTargetIds = new Set<number>();
|
|
2594
|
+
const matchingFinishedStatus = matchingStatusTargets
|
|
2595
|
+
.map(target => {
|
|
2596
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2597
|
+
const expectedPath = targetId === undefined ? undefined : expectedPaths.get(targetId);
|
|
2598
|
+
const expectedVersion =
|
|
2599
|
+
targetId === undefined
|
|
2600
|
+
? undefined
|
|
2601
|
+
: this.getExpectedProtocolV2TargetVersion(targetId);
|
|
2602
|
+
const payloadVersion = toProtocolV2FiniteNumber(target.payload_version);
|
|
2603
|
+
if (
|
|
2604
|
+
targetId === undefined ||
|
|
2605
|
+
matchingFinishedTargetIds.has(targetId) ||
|
|
2606
|
+
!isProtocolV2TargetStatusFinished(target.status) ||
|
|
2607
|
+
target.path !== expectedPath ||
|
|
2608
|
+
(expectedVersion !== undefined && payloadVersion !== expectedVersion)
|
|
2609
|
+
) {
|
|
2610
|
+
return undefined;
|
|
2611
|
+
}
|
|
2612
|
+
matchingFinishedTargetIds.add(targetId);
|
|
2613
|
+
return [targetId, target.path, payloadVersion ?? null] as const;
|
|
2614
|
+
})
|
|
2615
|
+
.filter((target): target is readonly [number, string, number | null] => !!target)
|
|
2616
|
+
.sort(([leftTargetId], [rightTargetId]) => leftTargetId - rightTargetId);
|
|
2617
|
+
const hasCompleteMatchingFinishedStatus =
|
|
2618
|
+
matchingFinishedStatus.length === expectedTargetIds.size &&
|
|
2619
|
+
matchingFinishedTargetIds.size === expectedTargetIds.size &&
|
|
2620
|
+
expectedTargetIds.size > 0;
|
|
2621
|
+
if (
|
|
2622
|
+
requireCurrentInstallStatus &&
|
|
2623
|
+
installReconnectObserved &&
|
|
2624
|
+
hasCompleteMatchingFinishedStatus
|
|
2625
|
+
) {
|
|
2626
|
+
const nextSnapshotKey = JSON.stringify(matchingFinishedStatus);
|
|
2627
|
+
if (nextSnapshotKey === finishedStatusSnapshotKey) {
|
|
2628
|
+
finishedStatusSnapshotPolls += 1;
|
|
2629
|
+
} else {
|
|
2630
|
+
finishedStatusSnapshotKey = nextSnapshotKey;
|
|
2631
|
+
finishedStatusSnapshotPolls = 1;
|
|
2632
|
+
}
|
|
2633
|
+
} else {
|
|
2634
|
+
finishedStatusSnapshotKey = undefined;
|
|
2635
|
+
finishedStatusSnapshotPolls = 0;
|
|
2636
|
+
}
|
|
2637
|
+
if (
|
|
2638
|
+
requireCurrentInstallStatus &&
|
|
2639
|
+
installReconnectObserved &&
|
|
2640
|
+
hasCompleteMatchingFinishedStatus &&
|
|
2641
|
+
!currentInstallStatusObserved &&
|
|
2642
|
+
!this.protocolV2InstallTerminalSuccessObserved &&
|
|
2643
|
+
!fastFinishedInstallProgressReported
|
|
2644
|
+
) {
|
|
2645
|
+
// A fast coprocessor install may finish while BLE is disconnected, so the
|
|
2646
|
+
// host never observes IN_PROGRESS. Enter the install UI while the matching
|
|
2647
|
+
// FINISHED snapshot is stabilized; completion still requires all polls.
|
|
2648
|
+
this.postProgressMessage(1, 'installingFirmware');
|
|
2649
|
+
fastFinishedInstallProgressReported = true;
|
|
2650
|
+
}
|
|
2651
|
+
const finishedAfterReconnectObserved =
|
|
2652
|
+
finishedStatusSnapshotPolls >= PROTOCOL_V2_INSTALL_FINISHED_AFTER_RECONNECT_POLLS;
|
|
2505
2653
|
const hasCurrentInstallEvidence =
|
|
2506
|
-
currentInstallStatusObserved ||
|
|
2654
|
+
currentInstallStatusObserved ||
|
|
2655
|
+
this.protocolV2InstallTerminalSuccessObserved ||
|
|
2656
|
+
finishedAfterReconnectObserved;
|
|
2657
|
+
if (hasCurrentInstallEvidence) {
|
|
2658
|
+
installEvidenceObserved = true;
|
|
2659
|
+
}
|
|
2507
2660
|
const shouldVerifyTargetCompletion =
|
|
2508
2661
|
!requireCurrentInstallStatus || hasCurrentInstallEvidence;
|
|
2509
2662
|
if (
|
|
@@ -2512,7 +2665,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2512
2665
|
statusTargets,
|
|
2513
2666
|
expectedTargetIds,
|
|
2514
2667
|
expectedPaths,
|
|
2515
|
-
requireCurrentInstallStatus &&
|
|
2668
|
+
requireCurrentInstallStatus &&
|
|
2669
|
+
!this.protocolV2InstallTerminalSuccessObserved &&
|
|
2670
|
+
!finishedAfterReconnectObserved
|
|
2516
2671
|
? liveTargetIds
|
|
2517
2672
|
: undefined
|
|
2518
2673
|
)
|
|
@@ -2628,6 +2783,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2628
2783
|
);
|
|
2629
2784
|
}
|
|
2630
2785
|
} else {
|
|
2786
|
+
if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ResponseTimeout(error)) {
|
|
2787
|
+
installTransportReleaseObserved = true;
|
|
2788
|
+
finishedStatusSnapshotKey = undefined;
|
|
2789
|
+
finishedStatusSnapshotPolls = 0;
|
|
2790
|
+
}
|
|
2631
2791
|
shouldReconnect = true;
|
|
2632
2792
|
deviceInfo = undefined;
|
|
2633
2793
|
bleInstallLinkReady = false;
|
|
@@ -2683,6 +2843,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2683
2843
|
return;
|
|
2684
2844
|
}
|
|
2685
2845
|
} catch (error) {
|
|
2846
|
+
if (isBleStaleBondHardwareError(error)) {
|
|
2847
|
+
throw error;
|
|
2848
|
+
}
|
|
2686
2849
|
Log.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
|
|
2687
2850
|
}
|
|
2688
2851
|
await this.protocolV2Reboot(DeviceRebootType.Normal);
|
|
@@ -2774,7 +2937,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2774
2937
|
'Protocol V2 device is still in bootloader mode'
|
|
2775
2938
|
);
|
|
2776
2939
|
} catch (error) {
|
|
2777
|
-
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2940
|
+
if (isBleStaleBondHardwareError(error) || this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2778
2941
|
throw error;
|
|
2779
2942
|
}
|
|
2780
2943
|
shouldReconnect = true;
|
|
@@ -3031,13 +3194,22 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
3031
3194
|
private async protocolV2Reboot(rebootType: DeviceRebootType) {
|
|
3032
3195
|
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
3033
3196
|
try {
|
|
3034
|
-
const res = await typedCall(
|
|
3035
|
-
|
|
3036
|
-
|
|
3197
|
+
const res = await typedCall(
|
|
3198
|
+
'DeviceReboot',
|
|
3199
|
+
'Success',
|
|
3200
|
+
{
|
|
3201
|
+
reboot_type: rebootType,
|
|
3202
|
+
},
|
|
3203
|
+
{ timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT }
|
|
3204
|
+
);
|
|
3037
3205
|
this.device.markProtocolV2Reboot(rebootType);
|
|
3038
3206
|
return res.message;
|
|
3039
3207
|
} catch (error) {
|
|
3040
|
-
if (
|
|
3208
|
+
if (
|
|
3209
|
+
isProtocolV2DeviceDisconnectedError(error) ||
|
|
3210
|
+
isProtocolV2ReconnectProbeError(error) ||
|
|
3211
|
+
isProtocolV2ResponseTimeout(error)
|
|
3212
|
+
) {
|
|
3041
3213
|
this.device.markProtocolV2Reboot(rebootType);
|
|
3042
3214
|
return { message: 'Device rebooted successfully' };
|
|
3043
3215
|
}
|
|
@@ -29,6 +29,12 @@ const wasResumed = (session: unknown) =>
|
|
|
29
29
|
'resumed' in session &&
|
|
30
30
|
(session as { resumed?: unknown }).resumed === true;
|
|
31
31
|
|
|
32
|
+
const wasWalletStatusRefreshed = (session: unknown) =>
|
|
33
|
+
!!session &&
|
|
34
|
+
typeof session === 'object' &&
|
|
35
|
+
'walletStatusRefreshed' in session &&
|
|
36
|
+
(session as { walletStatusRefreshed?: unknown }).walletStatusRefreshed === true;
|
|
37
|
+
|
|
32
38
|
const requireHiddenWalletResponse = (session: { passphraseState?: string }) => {
|
|
33
39
|
if (!session.passphraseState) {
|
|
34
40
|
throw ERRORS.TypedError(
|
|
@@ -87,12 +93,20 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
87
93
|
this.unlockPolicy = 'unlock-before-run';
|
|
88
94
|
this.skipForceUpdateCheck = true;
|
|
89
95
|
this.params = normalizeParams(this.payload as unknown as Record<string, unknown>);
|
|
96
|
+
this.protocolV2PreUnlockPinType =
|
|
97
|
+
this.params.mode === OpenWalletSessionMode.SelectHidden
|
|
98
|
+
? DeviceSessionPinType.Any
|
|
99
|
+
: DeviceSessionPinType.Main;
|
|
90
100
|
this.payload.useEmptyPassphrase = this.params.mode === OpenWalletSessionMode.Standard;
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
async run(): Promise<OpenWalletSessionPayload> {
|
|
94
104
|
const isProtocolV2 = this.device.isProtocolV2();
|
|
95
|
-
|
|
105
|
+
const reusePreflightStatus =
|
|
106
|
+
isProtocolV2 && this.protocolV2UnlockContext?.preflightStatusRefreshed === true;
|
|
107
|
+
let state = reusePreflightStatus
|
|
108
|
+
? await this.device.getDeviceState()
|
|
109
|
+
: await this.device.getDeviceState({ refreshSections: ['status'] });
|
|
96
110
|
let currentDeviceId = state.identity.deviceId;
|
|
97
111
|
const hasAuthoritativeProtocolV2WalletStatus = (candidate: typeof state) =>
|
|
98
112
|
candidate.status.unlocked === true &&
|
|
@@ -115,14 +129,25 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
115
129
|
currentDeviceId = state.identity.deviceId;
|
|
116
130
|
return state;
|
|
117
131
|
};
|
|
132
|
+
const resolveProtocolV2DeviceStateAfterSession = async (session: unknown) => {
|
|
133
|
+
if (!wasWalletStatusRefreshed(session)) {
|
|
134
|
+
return refreshProtocolV2DeviceState();
|
|
135
|
+
}
|
|
136
|
+
state = await this.device.getDeviceState();
|
|
137
|
+
currentDeviceId = state.identity.deviceId;
|
|
138
|
+
return state;
|
|
139
|
+
};
|
|
118
140
|
const ensureProtocolV2WalletStatus = async () => {
|
|
119
141
|
if (isProtocolV2 && !hasAuthoritativeProtocolV2WalletStatus(state)) {
|
|
120
|
-
await this.device.unlockDevice(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
142
|
+
await this.device.unlockDevice(
|
|
143
|
+
this.protocolV2PreUnlockPinType ?? DeviceSessionPinType.Main,
|
|
144
|
+
{
|
|
145
|
+
source: 'unlock-coordinator',
|
|
146
|
+
reason: 'device-locked',
|
|
147
|
+
deviceOnly: true,
|
|
148
|
+
method: 'openWalletSession',
|
|
149
|
+
}
|
|
150
|
+
);
|
|
126
151
|
requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState());
|
|
127
152
|
}
|
|
128
153
|
return state;
|
|
@@ -139,13 +164,16 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
139
164
|
selectMainWalletBeforeRestore:
|
|
140
165
|
!hasAuthoritativeProtocolV2WalletStatus(state) ||
|
|
141
166
|
state.status.unlockedAttachPin === true,
|
|
167
|
+
mainPinSelected: this.protocolV2UnlockContext?.preflightMainPinSelected,
|
|
142
168
|
})
|
|
143
169
|
: await getPassphraseStateWithRefreshDeviceInfo(this.device, {
|
|
144
170
|
onlyMainPin: true,
|
|
145
171
|
initSession: this.payload.initSession,
|
|
146
172
|
});
|
|
147
173
|
const refreshedState = isProtocolV2
|
|
148
|
-
? requireAuthoritativeProtocolV2WalletStatus(
|
|
174
|
+
? requireAuthoritativeProtocolV2WalletStatus(
|
|
175
|
+
await resolveProtocolV2DeviceStateAfterSession(session)
|
|
176
|
+
)
|
|
149
177
|
: state;
|
|
150
178
|
const deviceId = requireDeviceId();
|
|
151
179
|
if (
|
|
@@ -233,7 +261,9 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
233
261
|
})
|
|
234
262
|
: await getPassphraseStateWithRefreshDeviceInfo(this.device, { initSession: true });
|
|
235
263
|
const refreshedState = isProtocolV2
|
|
236
|
-
? requireAuthoritativeProtocolV2WalletStatus(
|
|
264
|
+
? requireAuthoritativeProtocolV2WalletStatus(
|
|
265
|
+
await resolveProtocolV2DeviceStateAfterSession(session)
|
|
266
|
+
)
|
|
237
267
|
: state;
|
|
238
268
|
const deviceId = requireDeviceId();
|
|
239
269
|
if (isProtocolV2 && refreshedState.status.passphraseProtection !== true) {
|
|
@@ -400,7 +400,8 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
|
|
|
400
400
|
this.payload.passphraseState,
|
|
401
401
|
useEmptyPassphrase,
|
|
402
402
|
this.payload.skipPassphraseCheck,
|
|
403
|
-
deriveCardano
|
|
403
|
+
deriveCardano,
|
|
404
|
+
this.protocolV2UnlockContext?.preflightMainPinSelected
|
|
404
405
|
);
|
|
405
406
|
if (!passphraseStateSafety) {
|
|
406
407
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
|
|
@@ -41,9 +41,15 @@ const FIRMWARE_UPDATE_CONFIRM = 'Firmware install confirmed';
|
|
|
41
41
|
*/
|
|
42
42
|
export const BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS = 5000;
|
|
43
43
|
|
|
44
|
-
type
|
|
44
|
+
type FirmwareProgressMetadata = Pick<
|
|
45
45
|
FirmwareProgress['payload'],
|
|
46
|
-
|
|
46
|
+
| 'transferredBytes'
|
|
47
|
+
| 'totalBytes'
|
|
48
|
+
| 'rateBytesPerSecond'
|
|
49
|
+
| 'elapsedMs'
|
|
50
|
+
| 'installTargetId'
|
|
51
|
+
| 'installPhase'
|
|
52
|
+
| 'installPhaseProgress'
|
|
47
53
|
>;
|
|
48
54
|
|
|
49
55
|
const isDeviceDisconnectedError = (error: unknown) => {
|
|
@@ -103,7 +109,7 @@ export class FirmwareUpdateBaseMethod<Params> extends BaseMethod<Params> {
|
|
|
103
109
|
postProgressMessage = (
|
|
104
110
|
progress: number,
|
|
105
111
|
progressType: IFirmwareUpdateProgressType,
|
|
106
|
-
metrics?:
|
|
112
|
+
metrics?: FirmwareProgressMetadata
|
|
107
113
|
) => {
|
|
108
114
|
this.postMessage(
|
|
109
115
|
createUiMessage(UI_REQUEST.FIRMWARE_PROGRESS, {
|
|
@@ -4,7 +4,13 @@ import { invalidParameter } from '../helpers/filesystemValidation';
|
|
|
4
4
|
import type { DeviceFirmwareUpdateStatusGetParams } from './helpers';
|
|
5
5
|
import type { DeviceFirmwareUpdateRecordFields } from '@onekeyfe/hd-transport';
|
|
6
6
|
|
|
7
|
-
const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = [
|
|
7
|
+
const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = [
|
|
8
|
+
'status',
|
|
9
|
+
'progress_percent',
|
|
10
|
+
'phase_info',
|
|
11
|
+
'payload_version',
|
|
12
|
+
'path',
|
|
13
|
+
] as const;
|
|
8
14
|
|
|
9
15
|
function normalizeStatusFields(
|
|
10
16
|
fields: DeviceFirmwareUpdateStatusGetParams['fields']
|
package/src/core/index.ts
CHANGED
|
@@ -651,7 +651,8 @@ const onCallDevice = async (
|
|
|
651
651
|
method.payload?.passphraseState,
|
|
652
652
|
method.payload?.useEmptyPassphrase,
|
|
653
653
|
method.payload?.skipPassphraseCheck,
|
|
654
|
-
hasDeriveCardano(method)
|
|
654
|
+
hasDeriveCardano(method),
|
|
655
|
+
method.protocolV2UnlockContext?.preflightMainPinSelected
|
|
655
656
|
);
|
|
656
657
|
|
|
657
658
|
// Double check, handles the special case of Touch/Pro
|
|
@@ -957,6 +958,14 @@ export function isMissingDetectedProtocolV2Error(method: BaseMethod, error: unkn
|
|
|
957
958
|
);
|
|
958
959
|
}
|
|
959
960
|
|
|
961
|
+
export function isProtocolV2PeerRemovedPairingError(method: BaseMethod, error: unknown) {
|
|
962
|
+
return (
|
|
963
|
+
method.payload.connectProtocol === 'V2' &&
|
|
964
|
+
(error as { errorCode?: unknown })?.errorCode ===
|
|
965
|
+
HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
|
|
960
969
|
/**
|
|
961
970
|
* If the Bluetooth connection times out, retry up to 6 times
|
|
962
971
|
* @param retryCount - Current retry count (default 0)
|
|
@@ -1258,7 +1267,8 @@ const ensureConnected = async (
|
|
|
1258
1267
|
HardwareErrorCode.BridgeNeedsPermission,
|
|
1259
1268
|
HardwareErrorCode.DeviceInterruptedFromUser,
|
|
1260
1269
|
HardwareErrorCode.CallQueueActionCancelled,
|
|
1261
|
-
].includes(error.errorCode)
|
|
1270
|
+
].includes(error.errorCode) ||
|
|
1271
|
+
isProtocolV2PeerRemovedPairingError(method, error)
|
|
1262
1272
|
) {
|
|
1263
1273
|
reject(error);
|
|
1264
1274
|
return;
|
|
@@ -1425,6 +1435,10 @@ const checkPassphraseEnableState = (method: BaseMethod, features?: Features) =>
|
|
|
1425
1435
|
const shouldCheckPassphraseState = (method: BaseMethod, device: Device) => {
|
|
1426
1436
|
if (!method.useDevicePassphraseState) return false;
|
|
1427
1437
|
|
|
1438
|
+
if (device.isProtocolV2() && method.payload?.useEmptyPassphrase === true) {
|
|
1439
|
+
return true;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1428
1442
|
return device.hasUsePassphrase();
|
|
1429
1443
|
};
|
|
1430
1444
|
|