@onekeyfe/hd-core 1.2.0-alpha.51 → 1.2.0-alpha.52

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.
@@ -0,0 +1,294 @@
1
+ import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
+
3
+ import { buildFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePlan';
4
+ import {
5
+ assertFirmwareUpdatePreparedPlanDeviceIdentity,
6
+ prepareFirmwareUpdatePlan,
7
+ } from '../../src/api/firmware/FirmwareUpdatePreparedPlan';
8
+
9
+ import type { Features } from '../../src/types';
10
+
11
+ const FIRMWARE_SHA256 = 'a'.repeat(64);
12
+
13
+ const BLE_SHA256 = 'b'.repeat(64);
14
+
15
+ const buildDesktopPlan = (
16
+ features: Partial<Features>,
17
+ options?: {
18
+ ble?: { status: string; release?: Record<string, unknown> };
19
+ firmwareStatus?: string;
20
+ }
21
+ ) =>
22
+ buildFirmwareUpdatePlan({
23
+ features: features as Features,
24
+ firmwareType: EFirmwareType.Universal,
25
+ platform: 'desktop',
26
+ firmware: {
27
+ status: options?.firmwareStatus ?? 'none',
28
+ release: {
29
+ url: 'https://firmware.onekey.so/classic/firmware.bin',
30
+ expectedSize: 4,
31
+ fingerprint: FIRMWARE_SHA256,
32
+ version: [3, 20, 0],
33
+ },
34
+ },
35
+ ble: options?.ble ?? {
36
+ status: 'valid',
37
+ release: undefined,
38
+ },
39
+ bootloader: {
40
+ status: 'valid',
41
+ release: undefined,
42
+ },
43
+ });
44
+
45
+ const bleNoneWithRelease = {
46
+ status: 'none',
47
+ release: {
48
+ url: 'https://firmware.onekey.so/classic/ble.bin',
49
+ webUpdate: 'https://firmware.onekey.so/classic/ble.bin',
50
+ expectedSize: 8,
51
+ fingerprint: BLE_SHA256,
52
+ version: [1, 5, 6],
53
+ },
54
+ };
55
+
56
+ // A classic1s that lost its firmware reports no serial in bootloader mode; the
57
+ // recovery flow must still be able to build (and later execute) a prepared plan.
58
+ const bootloaderClassic1sFeatures: Partial<Features> = {
59
+ deviceType: EDeviceType.Classic1s,
60
+ bootloaderMode: true,
61
+ firmwareVersion: '0.0.0',
62
+ bootloaderVersion: '2.1.2',
63
+ };
64
+
65
+ describe('firmware update plan identity in bootloader mode', () => {
66
+ test('builds a degraded-identity plan for a serial-less bootloader classic1s on desktop', () => {
67
+ const plan = buildDesktopPlan(bootloaderClassic1sFeatures);
68
+ expect(plan.deviceIdentity).toBe('unavailable');
69
+ expect(plan.executor).not.toBe('v4');
70
+ expect(plan.targetsToUpdate).toContain('firmware');
71
+ });
72
+
73
+ test('bootloader mode with intact firmware (status unknown) still gets the firmware artifact', () => {
74
+ const plan = buildDesktopPlan(
75
+ {
76
+ deviceType: EDeviceType.Classic1s,
77
+ bootloaderMode: true,
78
+ firmwareVersion: '2.1.2',
79
+ bootloaderVersion: '2.1.2',
80
+ },
81
+ { firmwareStatus: 'unknown' }
82
+ );
83
+ expect(plan.targetsToUpdate).toContain('firmware');
84
+ expect(plan.deviceIdentity).toBe('unavailable');
85
+ });
86
+
87
+ test('normal-mode status none with a release available still adds no artifacts', () => {
88
+ const plan = buildDesktopPlan({
89
+ deviceType: EDeviceType.Classic1s,
90
+ serialNo: 'CLA45F0023',
91
+ firmwareVersion: '3.19.0',
92
+ bootloaderVersion: '2.1.2',
93
+ });
94
+ expect(plan.targetsToUpdate).toEqual([]);
95
+ });
96
+
97
+ test('recovery plan carries the BLE artifact when the BLE version is unreadable in bootloader', () => {
98
+ const plan = buildDesktopPlan(bootloaderClassic1sFeatures, { ble: bleNoneWithRelease });
99
+ expect(plan.targetsToUpdate).toEqual(expect.arrayContaining(['firmware', 'ble']));
100
+ });
101
+
102
+ test('a normal-mode device without BLE hardware (status none) gets no BLE artifact', () => {
103
+ const plan = buildFirmwareUpdatePlan({
104
+ features: {
105
+ deviceType: EDeviceType.Mini,
106
+ serialNo: 'mini-device-id',
107
+ firmwareVersion: '3.9.0',
108
+ bootloaderVersion: '2.0.0',
109
+ } as Features,
110
+ firmwareType: EFirmwareType.Universal,
111
+ platform: 'desktop',
112
+ firmware: {
113
+ status: 'outdated',
114
+ release: {
115
+ url: 'https://firmware.onekey.so/mini/firmware.bin',
116
+ expectedSize: 4,
117
+ fingerprint: FIRMWARE_SHA256,
118
+ version: [3, 10, 0],
119
+ },
120
+ },
121
+ ble: { status: 'none', release: undefined },
122
+ bootloader: { status: 'valid', release: undefined },
123
+ });
124
+ expect(plan.targetsToUpdate).toEqual(['firmware']);
125
+ });
126
+
127
+ test('still rejects a serial-less normal-mode device on desktop', () => {
128
+ expect(() =>
129
+ buildDesktopPlan(
130
+ {
131
+ deviceType: EDeviceType.Classic1s,
132
+ firmwareVersion: '3.19.0',
133
+ bootloaderVersion: '2.1.2',
134
+ },
135
+ { firmwareStatus: 'outdated' }
136
+ )
137
+ ).toThrow(/stable device identity/);
138
+ });
139
+
140
+ test('keeps rejecting degraded identity for serial-less Pro (v3) in bootloader', () => {
141
+ expect(() =>
142
+ buildDesktopPlan(
143
+ {
144
+ deviceType: EDeviceType.Pro,
145
+ bootloaderMode: true,
146
+ firmwareVersion: '0.0.0',
147
+ bootloaderVersion: '2.8.0',
148
+ },
149
+ { firmwareStatus: 'none' }
150
+ )
151
+ ).toThrow(/stable device identity/);
152
+ });
153
+
154
+ test('degraded plans are v2-only', () => {
155
+ const plan = buildDesktopPlan(bootloaderClassic1sFeatures);
156
+ expect(plan.executor).toBe('v2');
157
+ });
158
+
159
+ describe('prepared plan device identity assertion', () => {
160
+ const prepareDegradedPlan = () => {
161
+ const plan = buildDesktopPlan(bootloaderClassic1sFeatures);
162
+ return prepareFirmwareUpdatePlan({
163
+ plan,
164
+ leaseRef: 'fwlease:00000000-0000-4000-8000-000000000001',
165
+ artifacts: [
166
+ {
167
+ artifactId: 'firmware',
168
+ artifact: {
169
+ artifactRef: `fw:${FIRMWARE_SHA256}`,
170
+ size: 4,
171
+ sha256: FIRMWARE_SHA256,
172
+ },
173
+ },
174
+ ],
175
+ });
176
+ };
177
+
178
+ test('accepts a degraded plan when the live device is also identity-less in bootloader mode', () => {
179
+ const preparedPlan = prepareDegradedPlan();
180
+ expect(() =>
181
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
182
+ preparedPlan,
183
+ deviceIdentity: undefined,
184
+ bootloaderMode: true,
185
+ deviceModel: String(EDeviceType.Classic1s),
186
+ })
187
+ ).not.toThrow();
188
+ });
189
+
190
+ test('rejects a degraded plan outside bootloader mode', () => {
191
+ const preparedPlan = prepareDegradedPlan();
192
+ expect(() =>
193
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
194
+ preparedPlan,
195
+ deviceIdentity: undefined,
196
+ bootloaderMode: false,
197
+ deviceModel: String(EDeviceType.Classic1s),
198
+ })
199
+ ).toThrow();
200
+ });
201
+
202
+ test('rejects a degraded plan when bootloader mode is not stated', () => {
203
+ const preparedPlan = prepareDegradedPlan();
204
+ expect(() =>
205
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
206
+ preparedPlan,
207
+ deviceIdentity: undefined,
208
+ deviceModel: String(EDeviceType.Classic1s),
209
+ })
210
+ ).toThrow();
211
+ });
212
+
213
+ test('rejects a degraded plan when the live device reports a real identity', () => {
214
+ const preparedPlan = prepareDegradedPlan();
215
+ expect(() =>
216
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
217
+ preparedPlan,
218
+ deviceIdentity: 'CLA45F0023',
219
+ bootloaderMode: true,
220
+ deviceModel: String(EDeviceType.Classic1s),
221
+ })
222
+ ).toThrow();
223
+ });
224
+
225
+ test('rejects a degraded plan on a different device model', () => {
226
+ const preparedPlan = prepareDegradedPlan();
227
+ expect(() =>
228
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
229
+ preparedPlan,
230
+ deviceIdentity: undefined,
231
+ bootloaderMode: true,
232
+ deviceModel: String(EDeviceType.Mini),
233
+ })
234
+ ).toThrow();
235
+ });
236
+
237
+ test('rejects a degraded plan when the live model is unstated', () => {
238
+ const preparedPlan = prepareDegradedPlan();
239
+ expect(() =>
240
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
241
+ preparedPlan,
242
+ deviceIdentity: undefined,
243
+ bootloaderMode: true,
244
+ })
245
+ ).toThrow();
246
+ });
247
+
248
+ test('a live serial equal to the reserved sentinel never matches a degraded plan', () => {
249
+ const preparedPlan = prepareDegradedPlan();
250
+ expect(() =>
251
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
252
+ preparedPlan,
253
+ deviceIdentity: 'unavailable',
254
+ bootloaderMode: false,
255
+ deviceModel: String(EDeviceType.Classic1s),
256
+ })
257
+ ).toThrow();
258
+ });
259
+
260
+ test('rejects a real-identity plan when the live device is identity-less in bootloader mode', () => {
261
+ const plan = buildDesktopPlan(
262
+ {
263
+ deviceType: EDeviceType.Classic1s,
264
+ serialNo: 'CLA45F0023',
265
+ firmwareVersion: '3.19.0',
266
+ bootloaderVersion: '2.1.2',
267
+ },
268
+ { firmwareStatus: 'outdated' }
269
+ );
270
+ const preparedPlan = prepareFirmwareUpdatePlan({
271
+ plan,
272
+ leaseRef: 'fwlease:00000000-0000-4000-8000-000000000002',
273
+ artifacts: [
274
+ {
275
+ artifactId: 'firmware',
276
+ artifact: {
277
+ artifactRef: `fw:${FIRMWARE_SHA256}`,
278
+ size: 4,
279
+ sha256: FIRMWARE_SHA256,
280
+ },
281
+ },
282
+ ],
283
+ });
284
+ expect(() =>
285
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
286
+ preparedPlan,
287
+ deviceIdentity: undefined,
288
+ bootloaderMode: true,
289
+ deviceModel: String(EDeviceType.Classic1s),
290
+ })
291
+ ).toThrow();
292
+ });
293
+ });
294
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"FirmwareUpdateV2.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV2.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,aAAa,EAKnB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAwB1C,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,UAAU,CAAC;AAEtD,OAAO,KAAK,EACV,sBAAsB,EACtB,yBAAyB,EAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AAE1F,KAAK,MAAM,GAAG;IACZ,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;IAC7C,eAAe,CAAC,EAAE,KAAK,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,yBAAyB,CAAC;KACrC,CAAC,CAAC;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,YAAY,CAAC,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,CAAC,EAAE,0BAA0B,CAAC,UAAU,CAAC,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,UAAU,GAAG,KAAK,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B,CAAC;AAkFF,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,UAAU,CAAC,MAAM,CAAC;IAC9D,YAAY,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,CAAQ;IAE1C,IAAI;IA6EJ,cAAc,YAAa,MAAM,UAS/B;YAEY,qCAAqC;IAkBnD,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS;YA6FvC,4BAA4B;IAuB1C,qBAAqB;IAUrB,uBAAuB,CAAC,UAAU,EAAE,MAAM;IAc1C,gCAAgC,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAAE,YAAY,EAAE,aAAa;IAwBtF,GAAG;CAyQV"}
1
+ {"version":3,"file":"FirmwareUpdateV2.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV2.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,aAAa,EAKnB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAyB1C,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,UAAU,CAAC;AAEtD,OAAO,KAAK,EACV,sBAAsB,EACtB,yBAAyB,EAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AAE1F,KAAK,MAAM,GAAG;IACZ,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;IAC7C,eAAe,CAAC,EAAE,KAAK,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,yBAAyB,CAAC;KACrC,CAAC,CAAC;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,YAAY,CAAC,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,CAAC,EAAE,0BAA0B,CAAC,UAAU,CAAC,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,UAAU,GAAG,KAAK,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B,CAAC;AAkFF,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,UAAU,CAAC,MAAM,CAAC;IAC9D,YAAY,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,CAAQ;IAE1C,IAAI;IA6EJ,cAAc,YAAa,MAAM,UAS/B;YAEY,qCAAqC;IAkBnD,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS;YA6FvC,4BAA4B;IAuB1C,qBAAqB;IAUrB,uBAAuB,CAAC,UAAU,EAAE,MAAM;IAc1C,gCAAgC,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAAE,YAAY,EAAE,aAAa;IAwBtF,GAAG;CA2QV"}
@@ -1 +1 @@
1
- {"version":3,"file":"FirmwareUpdatePlan.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwareUpdatePlan.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAS5F,OAAO,KAAK,EACV,kBAAkB,EAElB,6BAA6B,EAC7B,wBAAwB,EACzB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,KAAK,sBAAsB,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,WAAW,CAAC;AAyBjF,KAAK,gBAAgB,GAAG;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AA0GF,eAAO,MAAM,4BAA4B,UAAW,OAAO,KAAG,MAG3D,CAAC;AAkBJ,eAAO,MAAM,sCAAsC,UAC1C,OAAO,KACb,6BAA6B,EAa/B,CAAC;AAuBF,eAAO,MAAM,4BAA4B,+BAavC,CAAC;AAEH,eAAO,MAAM,0BAA0B,aAOrC,CAAC;AAYH,eAAO,MAAM,wBAAwB,UAAW,OAAO,KAAG,kBAoHzD,CAAC;AA4NF,eAAO,MAAM,uBAAuB;cASxB,QAAQ;kBACJ,aAAa;cACjB,sBAAsB;cACtB,gBAAgB;SACrB,gBAAgB;;;MAGnB,kBAiJH,CAAC"}
1
+ {"version":3,"file":"FirmwareUpdatePlan.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwareUpdatePlan.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAU5F,OAAO,KAAK,EACV,kBAAkB,EAElB,6BAA6B,EAC7B,wBAAwB,EACzB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,KAAK,sBAAsB,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,WAAW,CAAC;AAyBjF,KAAK,gBAAgB,GAAG;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AA0GF,eAAO,MAAM,4BAA4B,UAAW,OAAO,KAAG,MAG3D,CAAC;AAkBJ,eAAO,MAAM,sCAAsC,UAC1C,OAAO,KACb,6BAA6B,EAa/B,CAAC;AAuBF,eAAO,MAAM,4BAA4B,+BAavC,CAAC;AAEH,eAAO,MAAM,0BAA0B,aAOrC,CAAC;AAYH,eAAO,MAAM,wBAAwB,UAAW,OAAO,KAAG,kBAyHzD,CAAC;AA4NF,eAAO,MAAM,uBAAuB;cASxB,QAAQ;kBACJ,aAAa;cACjB,sBAAsB;cACtB,gBAAgB;SACrB,gBAAgB;;;MAGnB,kBAsKH,CAAC"}
@@ -8,9 +8,11 @@ export declare const prepareFirmwareUpdatePlan: ({ plan: value, leaseRef, artifa
8
8
  artifacts: FirmwareUpdatePreparedArtifactInput[];
9
9
  }) => FirmwareUpdatePreparedPlan;
10
10
  export declare const validateFirmwareUpdatePreparedPlan: (value: unknown) => FirmwareUpdatePreparedPlan;
11
- export declare const assertFirmwareUpdatePreparedPlanDeviceIdentity: ({ preparedPlan: value, deviceIdentity, }: {
11
+ export declare const assertFirmwareUpdatePreparedPlanDeviceIdentity: ({ preparedPlan: value, deviceIdentity: reportedIdentity, bootloaderMode, deviceModel, }: {
12
12
  preparedPlan: unknown;
13
13
  deviceIdentity: string | undefined;
14
+ bootloaderMode?: boolean | undefined;
15
+ deviceModel?: string | undefined;
14
16
  }) => FirmwareUpdatePreparedPlan;
15
17
  type FirmwareUpdatePreparedBinding = {
16
18
  target: FirmwareUpdatePlan['targetsToUpdate'][number];
@@ -1 +1 @@
1
- {"version":3,"file":"FirmwareUpdatePreparedPlan.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwareUpdatePreparedPlan.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,mCAAmC,EAEnC,0BAA0B,EAC3B,MAAM,4CAA4C,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAwB7E,eAAO,MAAM,6BAA6B,UAAW,OAAO,KAAG,MA8B9D,CAAC;AAgGF,eAAO,MAAM,yBAAyB;UAK9B,kBAAkB;cACd,MAAM;eACL,mCAAmC,EAAE;MAC9C,0BAmBH,CAAC;AAEF,eAAO,MAAM,kCAAkC,UAAW,OAAO,KAAG,0BAmGnE,CAAC;AAEF,eAAO,MAAM,8CAA8C;kBAI3C,OAAO;oBACL,MAAM,GAAG,SAAS;MAChC,0BAMH,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACnC,MAAM,EAAE,kBAAkB,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;IACtD,QAAQ,EAAE,yBAAyB,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAUF,eAAO,MAAM,uCAAuC;kBAQpC,OAAO;cACX,0BAA0B,CAAC,UAAU,CAAC;;;kBAGlC,0BAA0B,CAAC,iBAAiB,CAAC;cACjD,6BAA6B,EAAE;MACvC,0BAmDH,CAAC"}
1
+ {"version":3,"file":"FirmwareUpdatePreparedPlan.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwareUpdatePreparedPlan.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,mCAAmC,EAEnC,0BAA0B,EAC3B,MAAM,4CAA4C,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAwB7E,eAAO,MAAM,6BAA6B,UAAW,OAAO,KAAG,MA8B9D,CAAC;AAgGF,eAAO,MAAM,yBAAyB;UAK9B,kBAAkB;cACd,MAAM;eACL,mCAAmC,EAAE;MAC9C,0BAmBH,CAAC;AAEF,eAAO,MAAM,kCAAkC,UAAW,OAAO,KAAG,0BAmGnE,CAAC;AAEF,eAAO,MAAM,8CAA8C;kBAM3C,OAAO;oBACL,MAAM,GAAG,SAAS;;;MAKhC,0BAuBH,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACnC,MAAM,EAAE,kBAAkB,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;IACtD,QAAQ,EAAE,yBAAyB,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAUF,eAAO,MAAM,uCAAuC;kBAQpC,OAAO;cACX,0BAA0B,CAAC,UAAU,CAAC;;;kBAGlC,0BAA0B,CAAC,iBAAiB,CAAC;cACjD,6BAA6B,EAAE;MACvC,0BAmDH,CAAC"}
package/dist/index.js CHANGED
@@ -317,6 +317,13 @@ const resolveDeviceSerialNo = (features) => {
317
317
  const compatible = asCompatibleFeatures(features);
318
318
  return ((_a = firstNonEmptyString(compatible.serialNo, compatible.onekey_serial_no, compatible.onekey_serial, compatible.serial_no)) !== null && _a !== void 0 ? _a : '');
319
319
  };
320
+ const resolveDeviceBootloaderMode = (features) => {
321
+ var _a;
322
+ if (!features)
323
+ return false;
324
+ const compatible = asCompatibleFeatures(features);
325
+ return ((_a = compatible.bootloaderMode) !== null && _a !== void 0 ? _a : compatible.bootloader_mode) === true;
326
+ };
320
327
  const resolveDeviceType = (features) => {
321
328
  var _a, _b, _c, _d;
322
329
  if (!features || typeof features !== 'object') {
@@ -721,6 +728,7 @@ const assertFirmwareUpdatePlan = (value) => {
721
728
  plan.targetsToUpdate.some(target => legacyOnlyTargets.has(target))) ||
722
729
  (plan.artifacts.length > 0 &&
723
730
  (plan.platform === 'native' || plan.platform === 'desktop') &&
731
+ plan.executor !== 'v2' &&
724
732
  plan.deviceIdentity === 'unavailable')) {
725
733
  return planError('Firmware update plan executor contract is invalid');
726
734
  }
@@ -855,7 +863,11 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
855
863
  const firmwareRelease = asRelease(firmware);
856
864
  const validatedForceTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
857
865
  const forcedTargets = new Set(validatedForceTargets);
858
- const shouldUpdateFirmware = isUpgrade(firmware) || forcedTargets.has('firmware');
866
+ const bootloaderMode = resolveDeviceBootloaderMode(features);
867
+ const isRecoveryInstall = (release) => bootloaderMode &&
868
+ ((release === null || release === void 0 ? void 0 : release.status) === 'none' || (release === null || release === void 0 ? void 0 : release.status) === 'unknown') &&
869
+ !!asRelease(release);
870
+ const shouldUpdateFirmware = isUpgrade(firmware) || isRecoveryInstall(firmware) || forcedTargets.has('firmware');
859
871
  const shouldUpdateResource = isUpgrade(firmware) || forcedTargets.has('resource');
860
872
  if (executor === 'v4' &&
861
873
  validatedForceTargets.some(target => target === 'ble' || target === 'bootloader')) {
@@ -912,7 +924,7 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
912
924
  }
913
925
  }
914
926
  }
915
- if (isUpgrade(ble) || forcedTargets.has('ble')) {
927
+ if (isUpgrade(ble) || isRecoveryInstall(ble) || forcedTargets.has('ble')) {
916
928
  const bleRelease = asRelease(ble);
917
929
  artifacts.push(Object.assign(Object.assign({ artifactId: 'ble', role: 'ble', target: 'ble', url: assertArtifactUrl((_f = bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.webUpdate) !== null && _f !== void 0 ? _f : bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.url, 'BLE release'), container: 'raw' }, asIntegrity({
918
930
  size: bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.expectedSize,
@@ -929,10 +941,12 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
929
941
  artifacts,
930
942
  targetsToUpdate,
931
943
  });
932
- const deviceIdentity = getDeviceUUID(features);
944
+ const reportedIdentity = getDeviceUUID(features);
945
+ const deviceIdentity = reportedIdentity === 'unavailable' ? '' : reportedIdentity;
933
946
  if (artifacts.length > 0 &&
934
947
  (platform === 'native' || platform === 'desktop') &&
935
- !deviceIdentity) {
948
+ !deviceIdentity &&
949
+ !(bootloaderMode && executor === 'v2')) {
936
950
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Prepared firmware update requires a stable device identity', { firmwareUpdateCode: 'FirmwarePlanInvalid' });
937
951
  }
938
952
  const planWithoutDigest = {
@@ -1160,8 +1174,16 @@ const validateFirmwareUpdatePreparedPlan = (value) => {
1160
1174
  }
1161
1175
  return preparedPlan;
1162
1176
  };
1163
- const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({ preparedPlan: value, deviceIdentity, }) => {
1177
+ const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({ preparedPlan: value, deviceIdentity: reportedIdentity, bootloaderMode, deviceModel, }) => {
1164
1178
  const preparedPlan = validateFirmwareUpdatePreparedPlan(value);
1179
+ const deviceIdentity = reportedIdentity === 'unavailable' ? undefined : reportedIdentity;
1180
+ if (preparedPlan.deviceIdentity === 'unavailable' &&
1181
+ !deviceIdentity &&
1182
+ bootloaderMode === true &&
1183
+ deviceModel !== undefined &&
1184
+ preparedPlan.deviceModel === deviceModel) {
1185
+ return preparedPlan;
1186
+ }
1165
1187
  if (!deviceIdentity || preparedPlan.deviceIdentity !== deviceIdentity) {
1166
1188
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceCheckDeviceIdError);
1167
1189
  }
@@ -49193,6 +49215,8 @@ class FirmwareUpdateV2 extends BaseMethod {
49193
49215
  assertFirmwareUpdatePreparedPlanDeviceIdentity({
49194
49216
  preparedPlan: params.preparedPlan,
49195
49217
  deviceIdentity: getDeviceUUID(features) || undefined,
49218
+ bootloaderMode: resolveDeviceBootloaderMode(features),
49219
+ deviceModel: String(getDeviceType(features)),
49196
49220
  });
49197
49221
  }
49198
49222
  const deviceFirmwareType = device.getCurrentFirmwareType();
@@ -3,6 +3,7 @@ import type { PROTO } from '../constants';
3
3
  import type { Features, IDeviceType } from '../types';
4
4
  export type DeviceFeaturesInput = Features | PROTO.Features;
5
5
  export declare const resolveDeviceSerialNo: (features?: DeviceFeaturesInput) => string;
6
+ export declare const resolveDeviceBootloaderMode: (features?: DeviceFeaturesInput) => boolean;
6
7
  export declare const resolveDeviceType: (features?: DeviceFeaturesInput) => IDeviceType;
7
8
  export declare const resolveDeviceFirmwareType: (features?: DeviceFeaturesInput) => EFirmwareType;
8
9
  export declare const resolveDeviceBleName: (features?: DeviceFeaturesInput) => string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"deviceFeaturesCompat.d.ts","sourceRoot":"","sources":["../../src/utils/deviceFeaturesCompat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;AAoB5D,eAAO,MAAM,qBAAqB,cAAe,mBAAmB,KAAG,MAWtE,CAAC;AAEF,eAAO,MAAM,iBAAiB,cAAe,mBAAmB,KAAG,WAgDlE,CAAC;AAEF,eAAO,MAAM,yBAAyB,cAAe,mBAAmB,KAAG,aAsB1E,CAAC;AAEF,eAAO,MAAM,oBAAoB,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAM9E,CAAC;AAEF,eAAO,MAAM,4BAA4B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAgBtF,CAAC;AAEF,eAAO,MAAM,8BAA8B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAiBxF,CAAC;AAEF,eAAO,MAAM,yBAAyB,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAUnF,CAAC;AAEF,eAAO,MAAM,+BAA+B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IASzF,CAAC"}
1
+ {"version":3,"file":"deviceFeaturesCompat.d.ts","sourceRoot":"","sources":["../../src/utils/deviceFeaturesCompat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;AAoB5D,eAAO,MAAM,qBAAqB,cAAe,mBAAmB,KAAG,MAWtE,CAAC;AAEF,eAAO,MAAM,2BAA2B,cAAe,mBAAmB,KAAG,OAI5E,CAAC;AAEF,eAAO,MAAM,iBAAiB,cAAe,mBAAmB,KAAG,WAgDlE,CAAC;AAEF,eAAO,MAAM,yBAAyB,cAAe,mBAAmB,KAAG,aAsB1E,CAAC;AAEF,eAAO,MAAM,oBAAoB,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAM9E,CAAC;AAEF,eAAO,MAAM,4BAA4B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAgBtF,CAAC;AAEF,eAAO,MAAM,8BAA8B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAiBxF,CAAC;AAEF,eAAO,MAAM,yBAAyB,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAUnF,CAAC;AAEF,eAAO,MAAM,+BAA+B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IASzF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-core",
3
- "version": "1.2.0-alpha.51",
3
+ "version": "1.2.0-alpha.52",
4
4
  "description": "Core processes and APIs for communicating with OneKey hardware devices.",
5
5
  "author": "OneKey",
6
6
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
@@ -25,8 +25,8 @@
25
25
  "url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
26
26
  },
27
27
  "dependencies": {
28
- "@onekeyfe/hd-shared": "1.2.0-alpha.51",
29
- "@onekeyfe/hd-transport": "1.2.0-alpha.51",
28
+ "@onekeyfe/hd-shared": "1.2.0-alpha.52",
29
+ "@onekeyfe/hd-transport": "1.2.0-alpha.52",
30
30
  "axios": "1.15.2",
31
31
  "bignumber.js": "^9.0.2",
32
32
  "bytebuffer": "^5.0.1",
@@ -44,5 +44,5 @@
44
44
  "@types/w3c-web-usb": "^1.0.10",
45
45
  "@types/web-bluetooth": "^0.0.21"
46
46
  },
47
- "gitHead": "c947101d26542c9fd27264b9ddff7f7bba0ad24d"
47
+ "gitHead": "d8fd8f3d30c4ff3653d0379d5c3e1c100414b61e"
48
48
  }
@@ -21,7 +21,8 @@ import {
21
21
  uploadFirmwareFromSource,
22
22
  } from './firmware/uploadFirmware';
23
23
  import { BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS } from './firmware/FirmwareUpdateBaseMethod';
24
- import { LoggerNames, getDeviceUUID, getLogger, wait } from '../utils';
24
+ import { LoggerNames, getDeviceType, getDeviceUUID, getLogger, wait } from '../utils';
25
+ import { resolveDeviceBootloaderMode } from '../utils/deviceFeaturesCompat';
25
26
  import { FirmwareUpdateTipMessage, createUiMessage } from '../events/ui-request';
26
27
  import { DeviceModelToTypes } from '../types';
27
28
  import { DataManager } from '../data-manager';
@@ -429,6 +430,8 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
429
430
  assertFirmwareUpdatePreparedPlanDeviceIdentity({
430
431
  preparedPlan: params.preparedPlan,
431
432
  deviceIdentity: getDeviceUUID(features) || undefined,
433
+ bootloaderMode: resolveDeviceBootloaderMode(features),
434
+ deviceModel: String(getDeviceType(features)),
432
435
  });
433
436
  }
434
437
 
@@ -4,6 +4,7 @@ import { EDeviceType, EFirmwareType, ERRORS, HardwareErrorCode } from '@onekeyfe
4
4
  import semver from 'semver';
5
5
 
6
6
  import { getDeviceType, getDeviceUUID } from '../../utils/deviceInfoUtils';
7
+ import { resolveDeviceBootloaderMode } from '../../utils/deviceFeaturesCompat';
7
8
  import {
8
9
  getDeviceBootloaderVersion,
9
10
  getDeviceFirmwareVersion,
@@ -350,8 +351,13 @@ export const assertFirmwareUpdatePlan = (value: unknown): FirmwareUpdatePlan =>
350
351
  plan.targetsToUpdate.some(target =>
351
352
  legacyOnlyTargets.has(target as FirmwareUpdatePlanTarget)
352
353
  )) ||
354
+ // Only executor v2 services identity-less bootloader recovery (classic family);
355
+ // Pro (v3) and Pro2 (v4) report a serial even in bootloader mode, so their plans
356
+ // must always carry a real identity. Degraded v2 plans are bound to the live
357
+ // device at install time instead.
353
358
  (plan.artifacts.length > 0 &&
354
359
  (plan.platform === 'native' || plan.platform === 'desktop') &&
360
+ plan.executor !== 'v2' &&
355
361
  plan.deviceIdentity === 'unavailable')
356
362
  ) {
357
363
  return planError('Firmware update plan executor contract is invalid');
@@ -604,7 +610,19 @@ export const buildFirmwareUpdatePlan = ({
604
610
  const firmwareRelease = asRelease(firmware);
605
611
  const validatedForceTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
606
612
  const forcedTargets = new Set(validatedForceTargets);
607
- const shouldUpdateFirmware = isUpgrade(firmware) || forcedTargets.has('firmware');
613
+ const bootloaderMode = resolveDeviceBootloaderMode(features);
614
+ // Bootloader recovery: an erased device reports firmware status 'none', a device
615
+ // with intact firmware in bootloader mode reports 'unknown' (version fields carry
616
+ // the bootloader version), and neither can report its BLE version — yet the app
617
+ // treats bootloader mode as a mandatory reinstall. Only bootloader mode may treat
618
+ // these statuses as install targets: in normal mode 'none' also describes devices
619
+ // without the component at all (e.g. Mini has no BLE) and must stay excluded.
620
+ const isRecoveryInstall = (release: ReleaseSelection | undefined): boolean =>
621
+ bootloaderMode &&
622
+ (release?.status === 'none' || release?.status === 'unknown') &&
623
+ !!asRelease(release);
624
+ const shouldUpdateFirmware =
625
+ isUpgrade(firmware) || isRecoveryInstall(firmware) || forcedTargets.has('firmware');
608
626
  const shouldUpdateResource = isUpgrade(firmware) || forcedTargets.has('resource');
609
627
 
610
628
  if (
@@ -689,7 +707,7 @@ export const buildFirmwareUpdatePlan = ({
689
707
  }
690
708
  }
691
709
  }
692
- if (isUpgrade(ble) || forcedTargets.has('ble')) {
710
+ if (isUpgrade(ble) || isRecoveryInstall(ble) || forcedTargets.has('ble')) {
693
711
  const bleRelease = asRelease(ble);
694
712
  artifacts.push({
695
713
  artifactId: 'ble',
@@ -716,11 +734,20 @@ export const buildFirmwareUpdatePlan = ({
716
734
  targetsToUpdate,
717
735
  });
718
736
 
719
- const deviceIdentity = getDeviceUUID(features);
737
+ // 'unavailable' is the reserved degraded-identity sentinel; a device-reported
738
+ // serial must never be able to collide with it.
739
+ const reportedIdentity = getDeviceUUID(features);
740
+ const deviceIdentity = reportedIdentity === 'unavailable' ? '' : reportedIdentity;
741
+ // Bootloader-mode classic-family devices (executor v2, e.g. classic1s) cannot report
742
+ // a serial number, so their recovery plans fall back to the degraded 'unavailable'
743
+ // identity. Binding is then enforced against the live device at install time instead
744
+ // (assertFirmwareUpdatePreparedPlanDeviceIdentity), and plan integrity by the digest.
745
+ // Pro (v3) and Pro2 (v4) report a serial even in bootloader mode and stay strict.
720
746
  if (
721
747
  artifacts.length > 0 &&
722
748
  (platform === 'native' || platform === 'desktop') &&
723
- !deviceIdentity
749
+ !deviceIdentity &&
750
+ !(bootloaderMode && executor === 'v2')
724
751
  ) {
725
752
  throw ERRORS.TypedError(
726
753
  HardwareErrorCode.RuntimeError,
@@ -297,12 +297,35 @@ export const validateFirmwareUpdatePreparedPlan = (value: unknown): FirmwareUpda
297
297
 
298
298
  export const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({
299
299
  preparedPlan: value,
300
- deviceIdentity,
300
+ deviceIdentity: reportedIdentity,
301
+ bootloaderMode,
302
+ deviceModel,
301
303
  }: {
302
304
  preparedPlan: unknown;
303
305
  deviceIdentity: string | undefined;
306
+ /** Live device mode; only bootloader mode may accept a degraded-identity plan. */
307
+ bootloaderMode?: boolean;
308
+ /** Live device model; a degraded plan must target the same model. */
309
+ deviceModel?: string;
304
310
  }): FirmwareUpdatePreparedPlan => {
305
311
  const preparedPlan = validateFirmwareUpdatePreparedPlan(value);
312
+ // 'unavailable' is the reserved degraded sentinel — a device-reported serial equal
313
+ // to it must never satisfy the exact-match branch below.
314
+ const deviceIdentity = reportedIdentity === 'unavailable' ? undefined : reportedIdentity;
315
+ // Bootloader recovery: classic-family devices report no serial in bootloader mode,
316
+ // so a degraded plan is only acceptable when the live device is equally
317
+ // identity-less AND targets the same device model (identity equality implies the
318
+ // model match in the strict branch; the degraded branch must substitute it).
319
+ // A device that DOES report an identity must match the plan exactly.
320
+ if (
321
+ preparedPlan.deviceIdentity === 'unavailable' &&
322
+ !deviceIdentity &&
323
+ bootloaderMode === true &&
324
+ deviceModel !== undefined &&
325
+ preparedPlan.deviceModel === deviceModel
326
+ ) {
327
+ return preparedPlan;
328
+ }
306
329
  if (!deviceIdentity || preparedPlan.deviceIdentity !== deviceIdentity) {
307
330
  throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
308
331
  }
@@ -37,6 +37,12 @@ export const resolveDeviceSerialNo = (features?: DeviceFeaturesInput): string =>
37
37
  );
38
38
  };
39
39
 
40
+ export const resolveDeviceBootloaderMode = (features?: DeviceFeaturesInput): boolean => {
41
+ if (!features) return false;
42
+ const compatible = asCompatibleFeatures(features);
43
+ return (compatible.bootloaderMode ?? compatible.bootloader_mode) === true;
44
+ };
45
+
40
46
  export const resolveDeviceType = (features?: DeviceFeaturesInput): IDeviceType => {
41
47
  if (!features || typeof features !== 'object') {
42
48
  return EDeviceType.Unknown;