@onekeyfe/hd-core 1.2.0-alpha.62 → 1.2.0-alpha.64
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__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +102 -2
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
- package/dist/index.js +31 -5
- package/package.json +4 -4
- package/src/api/device/DeviceUpdateBootloader.ts +4 -1
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +47 -13
|
@@ -156,6 +156,102 @@ describe('firmware update plan identity in bootloader mode', () => {
|
|
|
156
156
|
expect(plan.executor).toBe('v2');
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
+
describe('degraded plan identity transition within one recovery workflow', () => {
|
|
160
|
+
// Bootloader recovery installs the main firmware while the device has no serial;
|
|
161
|
+
// it then reboots into normal mode and reports one, and the remaining phases of
|
|
162
|
+
// the SAME workflow reuse the same prepared plan.
|
|
163
|
+
let lease = 0;
|
|
164
|
+
const prepareRecoveryPlan = () => {
|
|
165
|
+
lease += 1;
|
|
166
|
+
const plan = buildDesktopPlan(bootloaderClassic1sFeatures);
|
|
167
|
+
return prepareFirmwareUpdatePlan({
|
|
168
|
+
plan,
|
|
169
|
+
leaseRef: `fwlease:00000000-0000-4000-8000-00000000000${lease}`,
|
|
170
|
+
artifacts: [
|
|
171
|
+
{
|
|
172
|
+
artifactId: 'firmware',
|
|
173
|
+
artifact: {
|
|
174
|
+
artifactRef: `fw:${FIRMWARE_SHA256}`,
|
|
175
|
+
size: 4,
|
|
176
|
+
sha256: FIRMWARE_SHA256,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
test('accepts the phase that runs after the device reboots and reports a serial', () => {
|
|
184
|
+
const preparedPlan = prepareRecoveryPlan();
|
|
185
|
+
// Phase 1: still in bootloader, no serial.
|
|
186
|
+
expect(() =>
|
|
187
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
188
|
+
preparedPlan,
|
|
189
|
+
deviceIdentity: undefined,
|
|
190
|
+
bootloaderMode: true,
|
|
191
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
192
|
+
})
|
|
193
|
+
).not.toThrow();
|
|
194
|
+
// Phase 2: firmware installed, device rebooted and now has an identity.
|
|
195
|
+
expect(() =>
|
|
196
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
197
|
+
preparedPlan,
|
|
198
|
+
deviceIdentity: 'CLA45F0023',
|
|
199
|
+
bootloaderMode: false,
|
|
200
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
201
|
+
})
|
|
202
|
+
).not.toThrow();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('pins the first identity it sees, so a different device cannot take over', () => {
|
|
206
|
+
const preparedPlan = prepareRecoveryPlan();
|
|
207
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
208
|
+
preparedPlan,
|
|
209
|
+
deviceIdentity: 'CLA45F0023',
|
|
210
|
+
bootloaderMode: false,
|
|
211
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
212
|
+
});
|
|
213
|
+
expect(() =>
|
|
214
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
215
|
+
preparedPlan,
|
|
216
|
+
deviceIdentity: 'CLA99Z9999',
|
|
217
|
+
bootloaderMode: false,
|
|
218
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
219
|
+
})
|
|
220
|
+
).toThrow();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('still refuses a different device model after the identity appears', () => {
|
|
224
|
+
const preparedPlan = prepareRecoveryPlan();
|
|
225
|
+
expect(() =>
|
|
226
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
227
|
+
preparedPlan,
|
|
228
|
+
deviceIdentity: 'MINI0000001',
|
|
229
|
+
bootloaderMode: false,
|
|
230
|
+
deviceModel: String(EDeviceType.Mini),
|
|
231
|
+
})
|
|
232
|
+
).toThrow();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('a fresh workflow does not inherit the previous workflow identity pin', () => {
|
|
236
|
+
const first = prepareRecoveryPlan();
|
|
237
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
238
|
+
preparedPlan: first,
|
|
239
|
+
deviceIdentity: 'CLA45F0023',
|
|
240
|
+
bootloaderMode: false,
|
|
241
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
242
|
+
});
|
|
243
|
+
const second = prepareRecoveryPlan();
|
|
244
|
+
expect(() =>
|
|
245
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
246
|
+
preparedPlan: second,
|
|
247
|
+
deviceIdentity: 'CLA99Z9999',
|
|
248
|
+
bootloaderMode: false,
|
|
249
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
250
|
+
})
|
|
251
|
+
).not.toThrow();
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
159
255
|
describe('prepared plan device identity assertion', () => {
|
|
160
256
|
const prepareDegradedPlan = () => {
|
|
161
257
|
const plan = buildDesktopPlan(bootloaderClassic1sFeatures);
|
|
@@ -210,7 +306,11 @@ describe('firmware update plan identity in bootloader mode', () => {
|
|
|
210
306
|
).toThrow();
|
|
211
307
|
});
|
|
212
308
|
|
|
213
|
-
|
|
309
|
+
// Superseded: a degraded plan MUST accept the first real identity, because that is
|
|
310
|
+
// the recovering device reporting its serial after the firmware phase rebooted it.
|
|
311
|
+
// The boundary that remains — a SECOND, different identity — is covered by
|
|
312
|
+
// 'pins the first identity it sees, so a different device cannot take over'.
|
|
313
|
+
test('binds to the identity the recovered device reports', () => {
|
|
214
314
|
const preparedPlan = prepareDegradedPlan();
|
|
215
315
|
expect(() =>
|
|
216
316
|
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
@@ -219,7 +319,7 @@ describe('firmware update plan identity in bootloader mode', () => {
|
|
|
219
319
|
bootloaderMode: true,
|
|
220
320
|
deviceModel: String(EDeviceType.Classic1s),
|
|
221
321
|
})
|
|
222
|
-
).toThrow();
|
|
322
|
+
).not.toThrow();
|
|
223
323
|
});
|
|
224
324
|
|
|
225
325
|
test('rejects a degraded plan on a different device model', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeviceUpdateBootloader.d.ts","sourceRoot":"","sources":["../../../src/api/device/DeviceUpdateBootloader.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"DeviceUpdateBootloader.d.ts","sourceRoot":"","sources":["../../../src/api/device/DeviceUpdateBootloader.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAgBhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAE7E,MAAM,CAAC,OAAO,OAAO,sBAAuB,SAAQ,wBAAwB,CAAC,GAAG,CAAC;IAC/E,IAAI;IAOE,iCAAiC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW;IAoBtE,2CAA2C,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB;IAcvF,qBAAqB,CAAC,EAC1B,MAAM,EACN,QAAQ,EACR,YAAY,EACZ,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,cAAc,GACvB,EAAE;QACD,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;QACpB,YAAY,EAAE,aAAa,CAAC;QAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,MAAM,CAAC,EAAE,kBAAkB,CAAC;KAC7B;IA0EK,GAAG;CA0EV"}
|
|
@@ -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;
|
|
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;AAuBF,eAAO,MAAM,8CAA8C;kBAM3C,OAAO;oBACL,MAAM,GAAG,SAAS;;;MAKhC,0BAoCH,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
|
@@ -1174,14 +1174,38 @@ const validateFirmwareUpdatePreparedPlan = (value) => {
|
|
|
1174
1174
|
}
|
|
1175
1175
|
return preparedPlan;
|
|
1176
1176
|
};
|
|
1177
|
+
const degradedPlanIdentityPins = new Map();
|
|
1178
|
+
const DEGRADED_PLAN_IDENTITY_PIN_LIMIT = 32;
|
|
1179
|
+
const pinDegradedPlanIdentity = (leaseRef, deviceIdentity) => {
|
|
1180
|
+
if (degradedPlanIdentityPins.size >= DEGRADED_PLAN_IDENTITY_PIN_LIMIT) {
|
|
1181
|
+
const oldest = degradedPlanIdentityPins.keys().next();
|
|
1182
|
+
if (!oldest.done) {
|
|
1183
|
+
degradedPlanIdentityPins.delete(oldest.value);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
degradedPlanIdentityPins.set(leaseRef, deviceIdentity);
|
|
1187
|
+
};
|
|
1177
1188
|
const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({ preparedPlan: value, deviceIdentity: reportedIdentity, bootloaderMode, deviceModel, }) => {
|
|
1178
1189
|
const preparedPlan = validateFirmwareUpdatePreparedPlan(value);
|
|
1179
1190
|
const deviceIdentity = reportedIdentity === 'unavailable' ? undefined : reportedIdentity;
|
|
1180
|
-
if (preparedPlan.deviceIdentity === 'unavailable'
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
preparedPlan.
|
|
1191
|
+
if (preparedPlan.deviceIdentity === 'unavailable') {
|
|
1192
|
+
if (deviceModel === undefined || preparedPlan.deviceModel !== deviceModel) {
|
|
1193
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
1194
|
+
}
|
|
1195
|
+
const pinnedIdentity = degradedPlanIdentityPins.get(preparedPlan.leaseRef);
|
|
1196
|
+
if (!deviceIdentity) {
|
|
1197
|
+
if (bootloaderMode !== true) {
|
|
1198
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
1199
|
+
}
|
|
1200
|
+
return preparedPlan;
|
|
1201
|
+
}
|
|
1202
|
+
if (!pinnedIdentity) {
|
|
1203
|
+
pinDegradedPlanIdentity(preparedPlan.leaseRef, deviceIdentity);
|
|
1204
|
+
return preparedPlan;
|
|
1205
|
+
}
|
|
1206
|
+
if (pinnedIdentity !== deviceIdentity) {
|
|
1207
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
1208
|
+
}
|
|
1185
1209
|
return preparedPlan;
|
|
1186
1210
|
}
|
|
1187
1211
|
if (!deviceIdentity || preparedPlan.deviceIdentity !== deviceIdentity) {
|
|
@@ -48639,6 +48663,8 @@ class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod {
|
|
|
48639
48663
|
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
48640
48664
|
preparedPlan: payload.preparedPlan,
|
|
48641
48665
|
deviceIdentity: getDeviceUUID(features) || undefined,
|
|
48666
|
+
bootloaderMode: resolveDeviceBootloaderMode(features),
|
|
48667
|
+
deviceModel: String(getDeviceType(features)),
|
|
48642
48668
|
});
|
|
48643
48669
|
assertFirmwareUpdatePreparedPlanBinding({
|
|
48644
48670
|
preparedPlan: payload.preparedPlan,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-core",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.64",
|
|
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.
|
|
29
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
28
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.64",
|
|
29
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.64",
|
|
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": "
|
|
47
|
+
"gitHead": "636cd3d9874820414cee6faa7e521f90a42ce6d5"
|
|
48
48
|
}
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
assertFirmwareUpdatePreparedPlanBinding,
|
|
16
16
|
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
17
17
|
} from '../firmware/FirmwareUpdatePreparedPlan';
|
|
18
|
-
import { getDeviceUUID } from '../../utils';
|
|
18
|
+
import { getDeviceType, getDeviceUUID } from '../../utils';
|
|
19
|
+
import { resolveDeviceBootloaderMode } from '../../utils/deviceFeaturesCompat';
|
|
19
20
|
|
|
20
21
|
import type { DeviceUpdateBootloaderParams } from '../../types/api/deviceUpdateBootloader';
|
|
21
22
|
import type { EFirmwareType } from '@onekeyfe/hd-shared';
|
|
@@ -167,6 +168,8 @@ export default class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod<any
|
|
|
167
168
|
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
168
169
|
preparedPlan: payload.preparedPlan,
|
|
169
170
|
deviceIdentity: getDeviceUUID(features) || undefined,
|
|
171
|
+
bootloaderMode: resolveDeviceBootloaderMode(features),
|
|
172
|
+
deviceModel: String(getDeviceType(features)),
|
|
170
173
|
});
|
|
171
174
|
assertFirmwareUpdatePreparedPlanBinding({
|
|
172
175
|
preparedPlan: payload.preparedPlan,
|
|
@@ -295,6 +295,27 @@ export const validateFirmwareUpdatePreparedPlan = (value: unknown): FirmwareUpda
|
|
|
295
295
|
return preparedPlan;
|
|
296
296
|
};
|
|
297
297
|
|
|
298
|
+
/**
|
|
299
|
+
* Identity observed on the live device while a degraded recovery plan runs, keyed by
|
|
300
|
+
* the plan's opaque lease. Bootloader recovery starts with no serial and the device
|
|
301
|
+
* reports one only after the firmware phase reboots it, so the later phases of the
|
|
302
|
+
* SAME workflow must be allowed to continue — but only for the device that first
|
|
303
|
+
* answered, never for a second one that appears mid-recovery.
|
|
304
|
+
*/
|
|
305
|
+
const degradedPlanIdentityPins = new Map<string, string>();
|
|
306
|
+
/** Keep the pin map from growing without bound across many workflows. */
|
|
307
|
+
const DEGRADED_PLAN_IDENTITY_PIN_LIMIT = 32;
|
|
308
|
+
|
|
309
|
+
const pinDegradedPlanIdentity = (leaseRef: string, deviceIdentity: string) => {
|
|
310
|
+
if (degradedPlanIdentityPins.size >= DEGRADED_PLAN_IDENTITY_PIN_LIMIT) {
|
|
311
|
+
const oldest = degradedPlanIdentityPins.keys().next();
|
|
312
|
+
if (!oldest.done) {
|
|
313
|
+
degradedPlanIdentityPins.delete(oldest.value);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
degradedPlanIdentityPins.set(leaseRef, deviceIdentity);
|
|
317
|
+
};
|
|
318
|
+
|
|
298
319
|
export const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({
|
|
299
320
|
preparedPlan: value,
|
|
300
321
|
deviceIdentity: reportedIdentity,
|
|
@@ -303,7 +324,7 @@ export const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({
|
|
|
303
324
|
}: {
|
|
304
325
|
preparedPlan: unknown;
|
|
305
326
|
deviceIdentity: string | undefined;
|
|
306
|
-
/** Live device mode;
|
|
327
|
+
/** Live device mode; a degraded plan may only START while the device is in bootloader. */
|
|
307
328
|
bootloaderMode?: boolean;
|
|
308
329
|
/** Live device model; a degraded plan must target the same model. */
|
|
309
330
|
deviceModel?: string;
|
|
@@ -312,20 +333,33 @@ export const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({
|
|
|
312
333
|
// 'unavailable' is the reserved degraded sentinel — a device-reported serial equal
|
|
313
334
|
// to it must never satisfy the exact-match branch below.
|
|
314
335
|
const deviceIdentity = reportedIdentity === 'unavailable' ? undefined : reportedIdentity;
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
336
|
+
|
|
337
|
+
if (preparedPlan.deviceIdentity === 'unavailable') {
|
|
338
|
+
// Identity equality implies a model match in the strict branch; the degraded
|
|
339
|
+
// branch has no identity to compare, so the model check must stand in for it.
|
|
340
|
+
if (deviceModel === undefined || preparedPlan.deviceModel !== deviceModel) {
|
|
341
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
342
|
+
}
|
|
343
|
+
const pinnedIdentity = degradedPlanIdentityPins.get(preparedPlan.leaseRef);
|
|
344
|
+
if (!deviceIdentity) {
|
|
345
|
+
// Still identity-less: only legitimate while the device sits in bootloader.
|
|
346
|
+
if (bootloaderMode !== true) {
|
|
347
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
348
|
+
}
|
|
349
|
+
return preparedPlan;
|
|
350
|
+
}
|
|
351
|
+
// The recovered device now reports a serial: bind this recovery to it, and hold
|
|
352
|
+
// every later phase to the same one.
|
|
353
|
+
if (!pinnedIdentity) {
|
|
354
|
+
pinDegradedPlanIdentity(preparedPlan.leaseRef, deviceIdentity);
|
|
355
|
+
return preparedPlan;
|
|
356
|
+
}
|
|
357
|
+
if (pinnedIdentity !== deviceIdentity) {
|
|
358
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
359
|
+
}
|
|
327
360
|
return preparedPlan;
|
|
328
361
|
}
|
|
362
|
+
|
|
329
363
|
if (!deviceIdentity || preparedPlan.deviceIdentity !== deviceIdentity) {
|
|
330
364
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
331
365
|
}
|