@onekeyfe/hd-core 1.2.0-alpha.184 → 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.
@@ -51,6 +51,7 @@ import {
51
51
  getProtocolV2UnknownErrorText,
52
52
  isProtocolV2DeviceDisconnectedError,
53
53
  } from './protocol-v2/helpers';
54
+ import { isProtocolV2ResponseTimeout } from './helpers/protocolV2FileWrite';
54
55
  import {
55
56
  openFirmwareByteSource,
56
57
  readFirmwareByteSourceFully,
@@ -62,6 +63,7 @@ import {
62
63
  assertFirmwareUpdatePreparedPlanDeviceIdentity,
63
64
  validateFirmwareUpdatePreparedPlan,
64
65
  } from './firmware/FirmwareUpdatePreparedPlan';
66
+
65
67
  import type {
66
68
  FirmwareArtifactReference,
67
69
  FirmwareUpdateV4Params,
@@ -91,6 +93,7 @@ const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
91
93
  const PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT = 15 * 1000;
92
94
  const PROTOCOL_V2_INSTALL_TIMEOUT = 5 * 60 * 1000;
93
95
  const PROTOCOL_V2_INSTALL_STATUS_INITIAL_DELAY = 1000;
96
+ const PROTOCOL_V2_INSTALL_FINISHED_AFTER_RECONNECT_POLLS = 4;
94
97
  const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
95
98
  const PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS = 1;
96
99
  const PROTOCOL_V2_TARGET_STATUS_FINISHED = 2;
@@ -189,6 +192,11 @@ const getProtocolV2DeviceTransferProgress = (
189
192
  type ProtocolV2FirmwareUpdateStatusTarget = {
190
193
  target_id: number | string;
191
194
  status?: number | string;
195
+ progress_percent?: number;
196
+ phase_info?: {
197
+ phase: number | string;
198
+ progress_percent: number;
199
+ };
192
200
  payload_version?: number;
193
201
  path?: string;
194
202
  };
@@ -409,6 +417,18 @@ const normalizeProtocolV2TargetStatus = (
409
417
  return undefined;
410
418
  };
411
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
+
412
432
  const normalizeProtocolV2Hex = (value?: string) => value?.replace(/^0x/i, '').toLowerCase();
413
433
 
414
434
  const versionArrayToNumber = (version?: IVersionArray) => {
@@ -466,6 +486,11 @@ const toProtocolV2FiniteNumber = (value: unknown) => {
466
486
  return undefined;
467
487
  };
468
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
+
469
494
  export const isProtocolV2FirmwareFingerprintValid = (
470
495
  binary: ArrayBuffer | Uint8Array,
471
496
  fingerprint: string | undefined
@@ -2310,6 +2335,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2310
2335
  for (const target of matchingTargets) {
2311
2336
  const targetId = normalizeProtocolV2TargetId(target.target_id);
2312
2337
  const pathConflictsWithCurrentInstall =
2338
+ targetId !== undefined &&
2313
2339
  Boolean(target.path) &&
2314
2340
  Boolean(resolvedExpectedPaths.get(targetId)) &&
2315
2341
  target.path !== resolvedExpectedPaths.get(targetId) &&
@@ -2373,16 +2399,54 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2373
2399
  }
2374
2400
 
2375
2401
  if (expectedTargetIds.size > 0 && matchingTargets.length > 0) {
2376
- const hasInProgressTarget = matchingTargets.some(target =>
2377
- isProtocolV2TargetStatusInProgress(target.status)
2378
- );
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
+ });
2379
2437
  const completedProgress = Math.floor(
2380
2438
  (completedTargetIds.size / expectedTargetIds.size) * 100
2381
2439
  );
2382
- // The protocol exposes no per-target percentage, so report coarse progress by
2383
- // completed targets and use 1% once work starts to keep the UI responsive.
2384
- const progress = Math.min(99, Math.max(completedProgress, hasInProgressTarget ? 1 : 0));
2385
- this.postProgressMessage(progress, 'installingFirmware');
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
+ }
2386
2450
  }
2387
2451
 
2388
2452
  return false;
@@ -2458,9 +2522,14 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2458
2522
  // The loader may release either USB or BLE as installation starts. Recovery reconnects
2459
2523
  // directly to status polling without generic Ping or DeviceInfo probes.
2460
2524
  let shouldReconnect = this.protocolV2InstallNeedsReconnect;
2525
+ let installTransportReleaseObserved = shouldReconnect;
2461
2526
  this.protocolV2InstallNeedsReconnect = false;
2462
2527
  let deviceInfo: ProtocolV2DeviceInfo | undefined;
2463
2528
  let bleInstallLinkReady = false;
2529
+ let installReconnectObserved = false;
2530
+ let finishedStatusSnapshotKey: string | undefined;
2531
+ let finishedStatusSnapshotPolls = 0;
2532
+ let fastFinishedInstallProgressReported = false;
2464
2533
  let installEvidenceObserved = this.protocolV2InstallTerminalSuccessObserved;
2465
2534
  let currentInstallStatusObserved = false;
2466
2535
  const liveTargetIds = new Set<number>();
@@ -2477,6 +2546,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2477
2546
  deviceInfo = isBleInstallReconnect
2478
2547
  ? undefined
2479
2548
  : await this.verifyProtocolV2ReconnectIdentity();
2549
+ if (installTransportReleaseObserved) {
2550
+ installReconnectObserved = true;
2551
+ }
2480
2552
  shouldReconnect = false;
2481
2553
  }
2482
2554
  let currentDeviceInfo = deviceInfo;
@@ -2487,6 +2559,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2487
2559
  {
2488
2560
  fields: {
2489
2561
  status: true,
2562
+ progress_percent: true,
2563
+ phase_info: true,
2490
2564
  payload_version: true,
2491
2565
  path: true,
2492
2566
  },
@@ -2516,8 +2590,73 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2516
2590
  const targetId = normalizeProtocolV2TargetId(target.target_id);
2517
2591
  return targetId !== undefined && expectedTargetIds.has(targetId);
2518
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;
2519
2653
  const hasCurrentInstallEvidence =
2520
- currentInstallStatusObserved || this.protocolV2InstallTerminalSuccessObserved;
2654
+ currentInstallStatusObserved ||
2655
+ this.protocolV2InstallTerminalSuccessObserved ||
2656
+ finishedAfterReconnectObserved;
2657
+ if (hasCurrentInstallEvidence) {
2658
+ installEvidenceObserved = true;
2659
+ }
2521
2660
  const shouldVerifyTargetCompletion =
2522
2661
  !requireCurrentInstallStatus || hasCurrentInstallEvidence;
2523
2662
  if (
@@ -2526,7 +2665,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2526
2665
  statusTargets,
2527
2666
  expectedTargetIds,
2528
2667
  expectedPaths,
2529
- requireCurrentInstallStatus && !this.protocolV2InstallTerminalSuccessObserved
2668
+ requireCurrentInstallStatus &&
2669
+ !this.protocolV2InstallTerminalSuccessObserved &&
2670
+ !finishedAfterReconnectObserved
2530
2671
  ? liveTargetIds
2531
2672
  : undefined
2532
2673
  )
@@ -2642,6 +2783,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2642
2783
  );
2643
2784
  }
2644
2785
  } else {
2786
+ if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ResponseTimeout(error)) {
2787
+ installTransportReleaseObserved = true;
2788
+ finishedStatusSnapshotKey = undefined;
2789
+ finishedStatusSnapshotPolls = 0;
2790
+ }
2645
2791
  shouldReconnect = true;
2646
2792
  deviceInfo = undefined;
2647
2793
  bleInstallLinkReady = false;
@@ -3048,13 +3194,22 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
3048
3194
  private async protocolV2Reboot(rebootType: DeviceRebootType) {
3049
3195
  const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
3050
3196
  try {
3051
- const res = await typedCall('DeviceReboot', 'Success', {
3052
- reboot_type: rebootType,
3053
- });
3197
+ const res = await typedCall(
3198
+ 'DeviceReboot',
3199
+ 'Success',
3200
+ {
3201
+ reboot_type: rebootType,
3202
+ },
3203
+ { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT }
3204
+ );
3054
3205
  this.device.markProtocolV2Reboot(rebootType);
3055
3206
  return res.message;
3056
3207
  } catch (error) {
3057
- if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ReconnectProbeError(error)) {
3208
+ if (
3209
+ isProtocolV2DeviceDisconnectedError(error) ||
3210
+ isProtocolV2ReconnectProbeError(error) ||
3211
+ isProtocolV2ResponseTimeout(error)
3212
+ ) {
3058
3213
  this.device.markProtocolV2Reboot(rebootType);
3059
3214
  return { message: 'Device rebooted successfully' };
3060
3215
  }
@@ -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 FirmwareTransferMetrics = Pick<
44
+ type FirmwareProgressMetadata = Pick<
45
45
  FirmwareProgress['payload'],
46
- 'transferredBytes' | 'totalBytes' | 'rateBytesPerSecond' | 'elapsedMs'
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?: FirmwareTransferMetrics
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 = ['status', 'payload_version', 'path'] as const;
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']
@@ -446,6 +446,9 @@
446
446
  "MessageType_DeviceCertificateSignature": 60423,
447
447
  "MessageType_DeviceCertificateSign": 60424,
448
448
  "MessageType_DeviceMiscUsbMscControl": 60440,
449
+ "MessageType_DeviceFindMyTokenState": 60450,
450
+ "MessageType_DeviceFindMyTokenUpdate": 60451,
451
+ "MessageType_DeviceFindMyTokenStateGet": 60452,
449
452
  "MessageType_DeviceInfoGet": 60600,
450
453
  "MessageType_DeviceInfo": 60601,
451
454
  "MessageType_DeviceStatusGet": 60602,
@@ -5404,6 +5407,10 @@
5404
5407
  "request_id": {
5405
5408
  "type": "bytes",
5406
5409
  "id": 5
5410
+ },
5411
+ "source_fingerprint": {
5412
+ "type": "uint32",
5413
+ "id": 6
5407
5414
  }
5408
5415
  }
5409
5416
  },
@@ -5539,6 +5546,14 @@
5539
5546
  "tx_type": {
5540
5547
  "type": "uint32",
5541
5548
  "id": 10
5549
+ },
5550
+ "expected_address": {
5551
+ "type": "bytes",
5552
+ "id": 12
5553
+ },
5554
+ "source_fingerprint": {
5555
+ "type": "uint32",
5556
+ "id": 13
5542
5557
  }
5543
5558
  }
5544
5559
  },
@@ -5619,6 +5634,14 @@
5619
5634
  "rule": "repeated",
5620
5635
  "type": "EthereumAccessListOneKey",
5621
5636
  "id": 11
5637
+ },
5638
+ "expected_address": {
5639
+ "type": "bytes",
5640
+ "id": 12
5641
+ },
5642
+ "source_fingerprint": {
5643
+ "type": "uint32",
5644
+ "id": 13
5622
5645
  }
5623
5646
  }
5624
5647
  },
@@ -5795,6 +5818,10 @@
5795
5818
  "chain_id": {
5796
5819
  "type": "uint64",
5797
5820
  "id": 3
5821
+ },
5822
+ "source_fingerprint": {
5823
+ "type": "uint32",
5824
+ "id": 4
5798
5825
  }
5799
5826
  }
5800
5827
  },
@@ -9474,6 +9501,10 @@
9474
9501
  "rule": "required",
9475
9502
  "type": "bytes",
9476
9503
  "id": 2
9504
+ },
9505
+ "source_fingerprint": {
9506
+ "type": "uint32",
9507
+ "id": 3
9477
9508
  }
9478
9509
  }
9479
9510
  },
@@ -9518,6 +9549,10 @@
9518
9549
  "application_domain": {
9519
9550
  "type": "bytes",
9520
9551
  "id": 5
9552
+ },
9553
+ "source_fingerprint": {
9554
+ "type": "uint32",
9555
+ "id": 6
9521
9556
  }
9522
9557
  }
9523
9558
  },
@@ -9535,6 +9570,10 @@
9535
9570
  "rule": "required",
9536
9571
  "type": "bytes",
9537
9572
  "id": 2
9573
+ },
9574
+ "source_fingerprint": {
9575
+ "type": "uint32",
9576
+ "id": 3
9538
9577
  }
9539
9578
  }
9540
9579
  },
@@ -11764,6 +11803,27 @@
11764
11803
  }
11765
11804
  }
11766
11805
  },
11806
+ "DeviceFindMyTokenUpdate": {
11807
+ "fields": {
11808
+ "token": {
11809
+ "rule": "required",
11810
+ "type": "bytes",
11811
+ "id": 1
11812
+ }
11813
+ }
11814
+ },
11815
+ "DeviceFindMyTokenStateGet": {
11816
+ "fields": {}
11817
+ },
11818
+ "DeviceFindMyTokenState": {
11819
+ "fields": {
11820
+ "burned": {
11821
+ "rule": "required",
11822
+ "type": "bool",
11823
+ "id": 1
11824
+ }
11825
+ }
11826
+ },
11767
11827
  "DeviceFactoryAck": {
11768
11828
  "values": {
11769
11829
  "FACTORY_ACK_SUCCESS": 0,
@@ -11893,6 +11953,13 @@
11893
11953
  "FW_MGMT_UPDATER_TASK_STATUS_FAILED_ENTRY_OUT_OF_BOUNDS": 10
11894
11954
  }
11895
11955
  },
11956
+ "DeviceFirmwareUpdatePhase": {
11957
+ "values": {
11958
+ "FW_MGMT_UPDATER_PHASE_PREPARE": 0,
11959
+ "FW_MGMT_UPDATER_PHASE_INSTALL": 1,
11960
+ "FW_MGMT_UPDATER_PHASE_VERIFY": 2
11961
+ }
11962
+ },
11896
11963
  "DeviceFirmwareTarget": {
11897
11964
  "fields": {
11898
11965
  "target_id": {
@@ -11917,7 +11984,26 @@
11917
11984
  }
11918
11985
  },
11919
11986
  "DeviceFirmwareUpdateRequest": {
11920
- "fields": {}
11987
+ "fields": {
11988
+ "reboot_after_update": {
11989
+ "type": "bool",
11990
+ "id": 1
11991
+ }
11992
+ }
11993
+ },
11994
+ "DeviceFirmwareUpdatePhaseInfo": {
11995
+ "fields": {
11996
+ "phase": {
11997
+ "rule": "required",
11998
+ "type": "DeviceFirmwareUpdatePhase",
11999
+ "id": 1
12000
+ },
12001
+ "progress_percent": {
12002
+ "rule": "required",
12003
+ "type": "uint32",
12004
+ "id": 2
12005
+ }
12006
+ }
11921
12007
  },
11922
12008
  "DeviceFirmwareUpdateRecord": {
11923
12009
  "fields": {
@@ -11930,6 +12016,14 @@
11930
12016
  "type": "DeviceFirmwareUpdateTaskStatus",
11931
12017
  "id": 10
11932
12018
  },
12019
+ "progress_percent": {
12020
+ "type": "uint32",
12021
+ "id": 11
12022
+ },
12023
+ "phase_info": {
12024
+ "type": "DeviceFirmwareUpdatePhaseInfo",
12025
+ "id": 12
12026
+ },
11933
12027
  "payload_version": {
11934
12028
  "type": "uint32",
11935
12029
  "id": 20
@@ -11946,6 +12040,14 @@
11946
12040
  "type": "bool",
11947
12041
  "id": 10
11948
12042
  },
12043
+ "progress_percent": {
12044
+ "type": "bool",
12045
+ "id": 11
12046
+ },
12047
+ "phase_info": {
12048
+ "type": "bool",
12049
+ "id": 12
12050
+ },
11949
12051
  "payload_version": {
11950
12052
  "type": "bool",
11951
12053
  "id": 20
@@ -12713,6 +12815,10 @@
12713
12815
  "text_id": {
12714
12816
  "type": "uint32",
12715
12817
  "id": 3
12818
+ },
12819
+ "text_arg": {
12820
+ "type": "string",
12821
+ "id": 4
12716
12822
  }
12717
12823
  }
12718
12824
  },
@@ -12780,6 +12886,10 @@
12780
12886
  "title_id": {
12781
12887
  "type": "uint32",
12782
12888
  "id": 8
12889
+ },
12890
+ "title_arg": {
12891
+ "type": "string",
12892
+ "id": 9
12783
12893
  }
12784
12894
  }
12785
12895
  },
@@ -189,6 +189,9 @@ export interface FirmwareProgress {
189
189
  totalBytes?: number;
190
190
  rateBytesPerSecond?: number;
191
191
  elapsedMs?: number;
192
+ installTargetId?: number;
193
+ installPhase?: 'prepare' | 'install' | 'verify';
194
+ installPhaseProgress?: number;
192
195
  };
193
196
  }
194
197
 
@@ -77,7 +77,8 @@ export type IProtocolV2Resources = {
77
77
  /** STM32 firmware config */
78
78
  export type IFirmwareReleaseInfo = {
79
79
  required: boolean;
80
- url: string;
80
+ /** Legacy release artifact URL. Protocol V2 releases use component URLs. */
81
+ url?: string;
81
82
  /**
82
83
  * Firmware type (bitcoinonly or universal)
83
84
  * This field is not present in the remote config, but will be inferred from the firmware field name
@@ -106,7 +107,8 @@ export type IFirmwareReleaseInfo = {
106
107
  bootloaderChangelog?: {
107
108
  [k in ILocale]: string;
108
109
  };
109
- fingerprint: string;
110
+ /** Legacy release artifact SHA-256. Protocol V2 releases use component fingerprints. */
111
+ fingerprint?: string;
110
112
  expectedSize?: number;
111
113
  version: IVersionArray;
112
114
  changelog: {