@onekeyfe/hd-core 1.2.0-alpha.18 → 1.2.0-alpha.19
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__/device-features-state.test.ts +78 -0
- package/__tests__/device-identity.test.ts +30 -0
- package/__tests__/device-pool-state.test.ts +113 -0
- package/__tests__/device-settings.test.ts +108 -0
- package/__tests__/device-state-contract.test.ts +27 -0
- package/__tests__/device-state-events.test.ts +163 -0
- package/__tests__/device-state-mapper.test.ts +222 -0
- package/__tests__/device-state-projector.test.ts +94 -0
- package/__tests__/device-state-store.test.ts +115 -0
- package/__tests__/device-wallet-session-store.test.ts +46 -0
- package/__tests__/deviceFeaturesUtils.test.ts +13 -1
- package/__tests__/deviceSettings.test.ts +54 -0
- package/__tests__/get-device-state.test.ts +367 -0
- package/__tests__/logBlockEvent.test.ts +13 -0
- package/__tests__/protocol-v2.test.ts +358 -413
- package/__tests__/public-device-state-api.test.ts +153 -0
- package/__tests__/refresh-device-state.test.ts +101 -0
- package/__tests__/request-context-logging.test.ts +16 -0
- package/__tests__/search-devices.test.ts +93 -0
- package/dist/api/ClearSessionCache.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetDeviceState.d.ts +7 -0
- package/dist/api/GetDeviceState.d.ts.map +1 -0
- package/dist/api/GetFeatures.d.ts +1 -1
- package/dist/api/GetFeatures.d.ts.map +1 -1
- package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
- package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
- package/dist/api/device/DeviceSettings.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -4
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/internal.d.ts +4 -0
- package/dist/api/internal.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts +0 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceSettingsGet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -1
- package/dist/api/utils.d.ts +2 -1
- package/dist/api/utils.d.ts.map +1 -1
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +22 -3
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceFeaturesState.d.ts +4 -0
- package/dist/device/DeviceFeaturesState.d.ts.map +1 -0
- package/dist/device/DevicePool.d.ts +1 -0
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts +13 -0
- package/dist/device/DeviceStateMapper.d.ts.map +1 -0
- package/dist/device/DeviceStateProjector.d.ts +8 -0
- package/dist/device/DeviceStateProjector.d.ts.map +1 -0
- package/dist/device/DeviceStateStore.d.ts +23 -0
- package/dist/device/DeviceStateStore.d.ts.map +1 -0
- package/dist/device/deviceIdentity.d.ts +5 -0
- package/dist/device/deviceIdentity.d.ts.map +1 -0
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/deviceProfile/index.d.ts +0 -1
- package/dist/deviceProfile/index.d.ts.map +1 -1
- package/dist/events/device.d.ts +7 -2
- package/dist/events/device.d.ts.map +1 -1
- package/dist/index.d.ts +688 -639
- package/dist/index.js +1274 -942
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/features.d.ts +1 -0
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +1 -1
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/deviceSettings.d.ts +8 -0
- package/dist/types/api/deviceSettings.d.ts.map +1 -1
- package/dist/types/api/getDeviceState.d.ts +12 -0
- package/dist/types/api/getDeviceState.d.ts.map +1 -0
- package/dist/types/api/index.d.ts +4 -7
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +1 -6
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +121 -1
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/deviceSettings.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/tracing.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/ClearSessionCache.ts +4 -0
- package/src/api/FirmwareUpdateV4.ts +15 -7
- package/src/api/GetDeviceState.ts +55 -0
- package/src/api/GetFeatures.ts +8 -3
- package/src/api/GetOnekeyFeatures.ts +3 -72
- package/src/api/GetPassphraseState.ts +3 -1
- package/src/api/SearchDevices.ts +25 -4
- package/src/api/device/DeviceRebootToBoardloader.ts +1 -0
- package/src/api/device/DeviceRebootToBootloader.ts +1 -0
- package/src/api/device/DeviceSettings.ts +28 -0
- package/src/api/index.ts +1 -4
- package/src/api/internal.ts +9 -0
- package/src/api/protocol-v2/DeviceInfoGet.ts +3 -5
- package/src/api/protocol-v2/DeviceReboot.ts +3 -1
- package/src/api/protocol-v2/DeviceSettingsGet.ts +3 -1
- package/src/api/protocol-v2/DeviceSettingsSet.ts +2 -0
- package/src/api/protocol-v2/DeviceStatusGet.ts +1 -0
- package/src/api/utils.ts +9 -3
- package/src/constants/index.ts +1 -4
- package/src/core/index.ts +7 -1
- package/src/data/messages/messages-protocol-v2.json +22 -13
- package/src/device/Device.ts +256 -78
- package/src/device/DeviceFeaturesState.ts +34 -0
- package/src/device/DevicePool.ts +35 -9
- package/src/device/DeviceStateMapper.ts +364 -0
- package/src/device/DeviceStateProjector.ts +92 -0
- package/src/device/DeviceStateStore.ts +242 -0
- package/src/device/deviceIdentity.ts +18 -0
- package/src/deviceProfile/buildDeviceFeatures.ts +166 -109
- package/src/deviceProfile/index.ts +0 -1
- package/src/events/device.ts +13 -1
- package/src/events/logBlockEvent.ts +1 -1
- package/src/inject.ts +4 -5
- package/src/protocols/protocol-v2/features.ts +18 -9
- package/src/protocols/protocol-v2/index.ts +1 -0
- package/src/protocols/protocol-v2/walletSession.ts +5 -7
- package/src/types/api/deviceSettings.ts +8 -0
- package/src/types/api/getDeviceState.ts +19 -0
- package/src/types/api/index.ts +3 -20
- package/src/types/api/protocolV2.ts +0 -24
- package/src/types/device.ts +148 -1
- package/src/utils/deviceFeaturesUtils.ts +5 -1
- package/src/utils/deviceSettings.ts +41 -0
- package/src/utils/tracing.ts +3 -1
- package/dist/api/GetDeviceInfo.d.ts +0 -9
- package/dist/api/GetDeviceInfo.d.ts.map +0 -1
- package/dist/deviceProfile/buildDeviceProfile.d.ts +0 -22
- package/dist/deviceProfile/buildDeviceProfile.d.ts.map +0 -1
- package/dist/types/api/getDeviceInfo.d.ts +0 -88
- package/dist/types/api/getDeviceInfo.d.ts.map +0 -1
- package/src/api/GetDeviceInfo.ts +0 -152
- package/src/deviceProfile/buildDeviceProfile.ts +0 -370
- package/src/types/api/getDeviceInfo.ts +0 -106
|
@@ -33,7 +33,6 @@ import EVMSignTypedData from '../src/api/evm/EVMSignTypedData';
|
|
|
33
33
|
import EVMSignMessageEIP712 from '../src/api/evm/EVMSignMessageEIP712';
|
|
34
34
|
import FirmwareUpdateV3 from '../src/api/FirmwareUpdateV3';
|
|
35
35
|
import FirmwareUpdateV4, { assertProtocolV2ReconnectIdentity } from '../src/api/FirmwareUpdateV4';
|
|
36
|
-
import GetDeviceInfo from '../src/api/GetDeviceInfo';
|
|
37
36
|
import GetPassphraseState from '../src/api/GetPassphraseState';
|
|
38
37
|
import GetOnekeyFeatures from '../src/api/GetOnekeyFeatures';
|
|
39
38
|
import { batchGetPublickeys } from '../src/api/helpers/batchGetPublickeys';
|
|
@@ -73,7 +72,6 @@ import {
|
|
|
73
72
|
import { runMethodWithUnlockRetry } from '../src/protocols/protocol-v2/unlockRetry';
|
|
74
73
|
import { BaseMethod } from '../src/api/BaseMethod';
|
|
75
74
|
import {
|
|
76
|
-
buildProfileFromProtocolV2,
|
|
77
75
|
buildProtocolV1FeaturesPayload,
|
|
78
76
|
buildProtocolV2FeaturesPayload,
|
|
79
77
|
} from '../src/deviceProfile';
|
|
@@ -301,6 +299,8 @@ const descriptor = {
|
|
|
301
299
|
*/
|
|
302
300
|
function stubDevice<T extends Record<string, any>>(device: T): T {
|
|
303
301
|
const d = device as any;
|
|
302
|
+
d.updateState ??= jest.fn();
|
|
303
|
+
d.markProtocolV2Reboot ??= jest.fn();
|
|
304
304
|
d.isProtocolV2 ??= () => d.originalDescriptor?.protocolType === 'V2';
|
|
305
305
|
d.getProtocol ??= () => (d.isProtocolV2() ? 'V2' : 'V1');
|
|
306
306
|
d.getCurrentDeviceType ??= () => getDeviceType(d.features);
|
|
@@ -337,6 +337,13 @@ function stubDevice<T extends Record<string, any>>(device: T): T {
|
|
|
337
337
|
});
|
|
338
338
|
return d.features;
|
|
339
339
|
};
|
|
340
|
+
d.updateFeaturesPatch ??= (patch: Record<string, unknown>) => {
|
|
341
|
+
d.features = {
|
|
342
|
+
...d.features,
|
|
343
|
+
...Object.fromEntries(Object.entries(patch).filter(([, value]) => value !== undefined)),
|
|
344
|
+
};
|
|
345
|
+
return d.features;
|
|
346
|
+
};
|
|
340
347
|
return device;
|
|
341
348
|
}
|
|
342
349
|
|
|
@@ -368,8 +375,7 @@ async function requestProtocolV2Features({
|
|
|
368
375
|
descriptor?: unknown;
|
|
369
376
|
}) {
|
|
370
377
|
const deviceInfo = await requestProtocolV2DeviceInfo({ commands });
|
|
371
|
-
|
|
372
|
-
return buildProtocolV2FeaturesPayload({ deviceInfo, deviceStatus });
|
|
378
|
+
return buildProtocolV2FeaturesPayload({ deviceInfo });
|
|
373
379
|
}
|
|
374
380
|
|
|
375
381
|
describe('Protocol V2 feature adapter', () => {
|
|
@@ -389,6 +395,26 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
389
395
|
expect(features.passphrase_protection).toBeUndefined();
|
|
390
396
|
});
|
|
391
397
|
|
|
398
|
+
test('preserves the last trusted passphrase setting while the device is locked', () => {
|
|
399
|
+
const features = buildProtocolV2FeaturesPayload({
|
|
400
|
+
deviceInfo: {
|
|
401
|
+
hw: { serial_no: 'P2-LOCKED' },
|
|
402
|
+
},
|
|
403
|
+
deviceStatus: {
|
|
404
|
+
unlocked: false,
|
|
405
|
+
passphrase_enabled: false,
|
|
406
|
+
},
|
|
407
|
+
previous: {
|
|
408
|
+
...buildProtocolV2FeaturesPayload({
|
|
409
|
+
deviceInfo: { hw: { serial_no: 'P2-LOCKED' } },
|
|
410
|
+
}),
|
|
411
|
+
passphraseProtection: true,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
expect(features.passphraseProtection).toBe(true);
|
|
416
|
+
});
|
|
417
|
+
|
|
392
418
|
test('builds dynamic features from DeviceStatus when DeviceInfo has no nested status', () => {
|
|
393
419
|
const deviceInfo: ProtocolV2DeviceInfo = {
|
|
394
420
|
protocol_version: 1,
|
|
@@ -563,13 +589,9 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
563
589
|
},
|
|
564
590
|
};
|
|
565
591
|
const features = normalizeProtocolV2Features(descriptor as any, deviceInfo);
|
|
566
|
-
const profile = buildProfileFromProtocolV2({ deviceInfo });
|
|
567
|
-
|
|
568
592
|
expect(features.mode).toBe('bootloader');
|
|
569
593
|
expect(features.bootloaderMode).toBe(true);
|
|
570
594
|
expect(features.initialized).toBeNull();
|
|
571
|
-
expect(profile.status.mode).toBe('bootloader');
|
|
572
|
-
expect(profile.status.bootloaderMode).toBe(true);
|
|
573
595
|
});
|
|
574
596
|
|
|
575
597
|
test('marks current romloader-shaped Protocol V2 DeviceInfo as romloader mode', () => {
|
|
@@ -588,14 +610,9 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
588
610
|
},
|
|
589
611
|
};
|
|
590
612
|
const features = normalizeProtocolV2Features(descriptor as any, deviceInfo);
|
|
591
|
-
const profile = buildProfileFromProtocolV2({ deviceInfo });
|
|
592
|
-
|
|
593
613
|
expect(features.mode).toBe('romloader');
|
|
594
614
|
expect(features.bootloaderMode).toBe(false);
|
|
595
615
|
expect(features.boardVersion).toBe('1.0.0');
|
|
596
|
-
expect(profile.status.mode).toBe('romloader');
|
|
597
|
-
expect(profile.status.bootloaderMode).toBe(false);
|
|
598
|
-
expect(profile.versions.board).toBe('1.0.0');
|
|
599
616
|
});
|
|
600
617
|
|
|
601
618
|
test('keeps the Protocol V2 main wallet on the default empty-passphrase context', async () => {
|
|
@@ -627,14 +644,17 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
627
644
|
expect(device.getInternalState()).toBeUndefined();
|
|
628
645
|
});
|
|
629
646
|
|
|
630
|
-
test('
|
|
647
|
+
test('keeps Protocol V2 on the standard wallet when passphrase protection is disabled', async () => {
|
|
631
648
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
632
649
|
(device as any).features = normalizeProtocolV2Features(
|
|
633
650
|
{ ...descriptor, protocolType: 'V2' } as any,
|
|
634
651
|
{ status: { passphrase_enabled: false, unlocked: true } }
|
|
635
652
|
);
|
|
636
653
|
const typedCall = jest.fn();
|
|
637
|
-
|
|
654
|
+
const promptPassphrase = jest
|
|
655
|
+
.fn()
|
|
656
|
+
.mockResolvedValue({ passphrase: 'hidden-wallet-with-disabled-flag' });
|
|
657
|
+
(device as any).commands = { typedCall, promptPassphrase };
|
|
638
658
|
|
|
639
659
|
await expect(getProtocolV2WalletSession(device)).resolves.toEqual({
|
|
640
660
|
passphraseState: undefined,
|
|
@@ -642,10 +662,51 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
642
662
|
unlockedAttachPin: false,
|
|
643
663
|
});
|
|
644
664
|
|
|
665
|
+
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
645
666
|
expect(typedCall).not.toHaveBeenCalled();
|
|
646
667
|
});
|
|
647
668
|
|
|
648
|
-
test('
|
|
669
|
+
test('preserves cached settings only while Protocol V2 physical identity stays the same', () => {
|
|
670
|
+
const previous = {
|
|
671
|
+
...normalizeProtocolV2Features(descriptor as any, {
|
|
672
|
+
hw: { serial_no: 'PR2-SAME' },
|
|
673
|
+
}),
|
|
674
|
+
deviceId: 'wallet-device-id',
|
|
675
|
+
label: 'Renamed Pro 2',
|
|
676
|
+
autoLockDelayMs: 60_000,
|
|
677
|
+
experimentalFeatures: true,
|
|
678
|
+
passphraseProtection: true,
|
|
679
|
+
attachToPinEnabled: true,
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
const refreshed = buildProtocolV2FeaturesPayload({
|
|
683
|
+
deviceInfo: { hw: { serial_no: 'PR2-SAME' } },
|
|
684
|
+
previous,
|
|
685
|
+
});
|
|
686
|
+
expect(refreshed).toMatchObject({
|
|
687
|
+
deviceId: 'wallet-device-id',
|
|
688
|
+
label: 'Renamed Pro 2',
|
|
689
|
+
autoLockDelayMs: 60_000,
|
|
690
|
+
experimentalFeatures: true,
|
|
691
|
+
passphraseProtection: true,
|
|
692
|
+
attachToPinEnabled: true,
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
const replacedDevice = buildProtocolV2FeaturesPayload({
|
|
696
|
+
deviceInfo: { hw: { serial_no: 'PR2-OTHER' } },
|
|
697
|
+
previous,
|
|
698
|
+
});
|
|
699
|
+
expect(replacedDevice).toMatchObject({
|
|
700
|
+
deviceId: null,
|
|
701
|
+
label: null,
|
|
702
|
+
autoLockDelayMs: null,
|
|
703
|
+
experimentalFeatures: null,
|
|
704
|
+
passphraseProtection: null,
|
|
705
|
+
attachToPinEnabled: null,
|
|
706
|
+
});
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
test('uses refreshed passphrase status after unlocking before selecting a Protocol V2 wallet', async () => {
|
|
649
710
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
650
711
|
(device as any).features = normalizeProtocolV2Features(
|
|
651
712
|
{ ...descriptor, protocolType: 'V2' } as any,
|
|
@@ -656,8 +717,23 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
656
717
|
},
|
|
657
718
|
}
|
|
658
719
|
);
|
|
659
|
-
const typedCall = jest.fn()
|
|
660
|
-
|
|
720
|
+
const typedCall = jest.fn().mockImplementation((request: string) => {
|
|
721
|
+
if (request === 'DeviceSessionOpen') {
|
|
722
|
+
return Promise.resolve({
|
|
723
|
+
message: {
|
|
724
|
+
btc_test_address: 'state-after-unlock',
|
|
725
|
+
session_id: 'session-after-unlock',
|
|
726
|
+
},
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
if (request === 'DeviceStatusGet') {
|
|
730
|
+
return Promise.resolve({
|
|
731
|
+
message: { passphrase_enabled: false, unlocked: true },
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
735
|
+
});
|
|
736
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'hidden-after-unlock' });
|
|
661
737
|
const unlockDevice = jest.spyOn(device, 'unlockDevice').mockImplementation(() => {
|
|
662
738
|
(device as any).features = {
|
|
663
739
|
...(device as any).features,
|
|
@@ -874,11 +950,10 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
874
950
|
expect(() => method.init()).toThrow();
|
|
875
951
|
});
|
|
876
952
|
|
|
877
|
-
test('routes
|
|
953
|
+
test('routes public onboarding and session methods through CoreApi', async () => {
|
|
878
954
|
const call = jest.fn().mockResolvedValue({ success: true, payload: {} });
|
|
879
955
|
const api = createCoreApi(call as any);
|
|
880
956
|
|
|
881
|
-
await api.deviceStatusGet('connect-id', { retryCount: 1 });
|
|
882
957
|
await api.deviceGetOnboardingStatus('connect-id', { retryCount: 1 });
|
|
883
958
|
await api.deviceSessionOpen('connect-id', {
|
|
884
959
|
retryCount: 1,
|
|
@@ -886,16 +961,11 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
886
961
|
});
|
|
887
962
|
|
|
888
963
|
expect(call).toHaveBeenNthCalledWith(1, {
|
|
889
|
-
method: 'deviceStatusGet',
|
|
890
|
-
connectId: 'connect-id',
|
|
891
|
-
retryCount: 1,
|
|
892
|
-
});
|
|
893
|
-
expect(call).toHaveBeenNthCalledWith(2, {
|
|
894
964
|
method: 'deviceGetOnboardingStatus',
|
|
895
965
|
connectId: 'connect-id',
|
|
896
966
|
retryCount: 1,
|
|
897
967
|
});
|
|
898
|
-
expect(call).toHaveBeenNthCalledWith(
|
|
968
|
+
expect(call).toHaveBeenNthCalledWith(2, {
|
|
899
969
|
method: 'deviceSessionOpen',
|
|
900
970
|
connectId: 'connect-id',
|
|
901
971
|
retryCount: 1,
|
|
@@ -1190,6 +1260,8 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1190
1260
|
|
|
1191
1261
|
expect(device.features?.mode).toBe('bootloader');
|
|
1192
1262
|
expect(device.features?.bootloaderMode).toBe(true);
|
|
1263
|
+
expect(device.features?.initialized).toBeNull();
|
|
1264
|
+
expect(device.features?.unlocked).toBeNull();
|
|
1193
1265
|
expect(device.features?.raw?.protocolV2DeviceStatus).toBeUndefined();
|
|
1194
1266
|
});
|
|
1195
1267
|
|
|
@@ -1281,6 +1353,40 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1281
1353
|
expect(getFeatures).not.toHaveBeenCalled();
|
|
1282
1354
|
});
|
|
1283
1355
|
|
|
1356
|
+
test('uses the Pro2 standard wallet without passphraseState when passphrase is disabled', async () => {
|
|
1357
|
+
const features = {
|
|
1358
|
+
deviceId: 'pro2-device-id',
|
|
1359
|
+
deviceType: 'pro2',
|
|
1360
|
+
firmwareVersion: '9.9.9',
|
|
1361
|
+
passphraseProtection: false,
|
|
1362
|
+
unlocked: true,
|
|
1363
|
+
};
|
|
1364
|
+
const typedCall = jest.fn();
|
|
1365
|
+
const updateInternalState = jest.fn();
|
|
1366
|
+
const method = new GetPassphraseState({
|
|
1367
|
+
payload: {
|
|
1368
|
+
method: 'getPassphraseState',
|
|
1369
|
+
connectId: 'connect-id',
|
|
1370
|
+
},
|
|
1371
|
+
});
|
|
1372
|
+
method.device = stubDevice({
|
|
1373
|
+
originalDescriptor: { ...descriptor, protocolType: 'V2' },
|
|
1374
|
+
features,
|
|
1375
|
+
commands: {
|
|
1376
|
+
typedCall,
|
|
1377
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphrase: 'mock-hidden-wallet' }),
|
|
1378
|
+
},
|
|
1379
|
+
updateInternalState,
|
|
1380
|
+
getCurrentDeviceType: () => 'pro2',
|
|
1381
|
+
getCurrentDeviceId: () => 'pro2-device-id',
|
|
1382
|
+
getCurrentPassphraseProtection: () => false,
|
|
1383
|
+
}) as any;
|
|
1384
|
+
|
|
1385
|
+
await expect(method.run()).resolves.toBeUndefined();
|
|
1386
|
+
expect(typedCall).not.toHaveBeenCalled();
|
|
1387
|
+
expect(updateInternalState).not.toHaveBeenCalled();
|
|
1388
|
+
});
|
|
1389
|
+
|
|
1284
1390
|
test('honors initSession when getting Pro2 passphrase state', async () => {
|
|
1285
1391
|
const features = {
|
|
1286
1392
|
deviceId: 'pro2-device-id',
|
|
@@ -1378,9 +1484,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1378
1484
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1379
1485
|
}
|
|
1380
1486
|
);
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1487
|
+
device.updateState(
|
|
1488
|
+
{
|
|
1489
|
+
versions: { firmware: '4.15.0' },
|
|
1490
|
+
status: { passphraseProtection: true, unlocked: true },
|
|
1491
|
+
},
|
|
1492
|
+
'compatibility'
|
|
1493
|
+
);
|
|
1384
1494
|
(device as any).commands = {
|
|
1385
1495
|
typedCall,
|
|
1386
1496
|
promptPassphrase: jest.fn().mockResolvedValue({ passphrase: 'state-auto-secret' }),
|
|
@@ -1541,6 +1651,8 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1541
1651
|
uuid: 'CACHED-SERIAL',
|
|
1542
1652
|
deviceId: null,
|
|
1543
1653
|
bleName: 'Cached BLE',
|
|
1654
|
+
name: 'Cached BLE',
|
|
1655
|
+
displayName: 'Cached Label',
|
|
1544
1656
|
label: 'Cached Label',
|
|
1545
1657
|
deviceType: 'pro2',
|
|
1546
1658
|
});
|
|
@@ -1569,22 +1681,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1569
1681
|
|
|
1570
1682
|
test('initializes Protocol V2 features from lightweight DeviceInfoGet', async () => {
|
|
1571
1683
|
const commands = {
|
|
1572
|
-
typedCall: jest
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
})
|
|
1580
|
-
.mockResolvedValueOnce({
|
|
1581
|
-
type: 'DeviceStatus',
|
|
1582
|
-
message: {
|
|
1583
|
-
device_id: 'PRO2-STATUS-ID',
|
|
1584
|
-
init_states: true,
|
|
1585
|
-
passphrase_enabled: true,
|
|
1586
|
-
},
|
|
1587
|
-
}),
|
|
1684
|
+
typedCall: jest.fn().mockResolvedValueOnce({
|
|
1685
|
+
type: 'DeviceInfo',
|
|
1686
|
+
message: {
|
|
1687
|
+
hw: { serial_no: 'PR2SERIAL' },
|
|
1688
|
+
fw: { application: { version: '1.2.3' } },
|
|
1689
|
+
},
|
|
1690
|
+
}),
|
|
1588
1691
|
};
|
|
1589
1692
|
|
|
1590
1693
|
const features = await requestProtocolV2Features({
|
|
@@ -1592,12 +1695,12 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1592
1695
|
descriptor: descriptor as any,
|
|
1593
1696
|
});
|
|
1594
1697
|
|
|
1595
|
-
expect(features.deviceId).
|
|
1596
|
-
expect(features.initialized).
|
|
1698
|
+
expect(features.deviceId).toBeNull();
|
|
1699
|
+
expect(features.initialized).toBeNull();
|
|
1597
1700
|
expect(features.passphraseProtection).toBeNull();
|
|
1598
|
-
expect(
|
|
1599
|
-
expect(commands.typedCall).
|
|
1600
|
-
|
|
1701
|
+
expect(features.bootloaderMode).toBe(false);
|
|
1702
|
+
expect(commands.typedCall).toHaveBeenCalledTimes(1);
|
|
1703
|
+
expect(commands.typedCall).toHaveBeenCalledWith(
|
|
1601
1704
|
'DeviceInfoGet',
|
|
1602
1705
|
'DeviceInfo',
|
|
1603
1706
|
{
|
|
@@ -1613,7 +1716,6 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1613
1716
|
},
|
|
1614
1717
|
{ timeoutMs: PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS }
|
|
1615
1718
|
);
|
|
1616
|
-
expect(commands.typedCall).toHaveBeenNthCalledWith(2, 'DeviceStatusGet', 'DeviceStatus', {});
|
|
1617
1719
|
});
|
|
1618
1720
|
|
|
1619
1721
|
test('fails initialization when Protocol V2 DeviceInfoGet fails', async () => {
|
|
@@ -1629,266 +1731,6 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1629
1731
|
).rejects.toThrow('DeviceInfo not supported');
|
|
1630
1732
|
});
|
|
1631
1733
|
|
|
1632
|
-
test('refreshes Protocol V2 basic device info with the lightweight request', async () => {
|
|
1633
|
-
const typedCall = jest
|
|
1634
|
-
.fn()
|
|
1635
|
-
.mockResolvedValueOnce({
|
|
1636
|
-
type: 'DeviceInfo',
|
|
1637
|
-
message: {
|
|
1638
|
-
hw: { serial_no: 'PR2SERIAL' },
|
|
1639
|
-
fw: {
|
|
1640
|
-
application: {
|
|
1641
|
-
version: '5.6.7',
|
|
1642
|
-
},
|
|
1643
|
-
},
|
|
1644
|
-
coprocessor: {
|
|
1645
|
-
application: {
|
|
1646
|
-
version: '8.9.10',
|
|
1647
|
-
},
|
|
1648
|
-
bt_adv_name: 'Raw Pro2 BLE',
|
|
1649
|
-
},
|
|
1650
|
-
},
|
|
1651
|
-
})
|
|
1652
|
-
.mockResolvedValueOnce({
|
|
1653
|
-
type: 'DeviceStatus',
|
|
1654
|
-
message: {
|
|
1655
|
-
device_id: 'PRO2-BASIC-ID',
|
|
1656
|
-
unlocked_by_attach_to_pin: true,
|
|
1657
|
-
init_states: true,
|
|
1658
|
-
passphrase_enabled: true,
|
|
1659
|
-
},
|
|
1660
|
-
});
|
|
1661
|
-
const method = new GetDeviceInfo({
|
|
1662
|
-
id: 1,
|
|
1663
|
-
payload: {
|
|
1664
|
-
method: 'getDeviceInfo',
|
|
1665
|
-
scope: 'basic',
|
|
1666
|
-
refresh: true,
|
|
1667
|
-
},
|
|
1668
|
-
});
|
|
1669
|
-
method.init();
|
|
1670
|
-
(method as any).device = stubDevice({
|
|
1671
|
-
originalDescriptor: { ...descriptor, protocolType: 'V2' },
|
|
1672
|
-
features: {
|
|
1673
|
-
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
1674
|
-
device_id: 'CACHED-ID',
|
|
1675
|
-
serial_no: 'CACHED-SERIAL',
|
|
1676
|
-
label: 'Cached Pro2',
|
|
1677
|
-
onekey_firmware_version: '1.2.3',
|
|
1678
|
-
onekey_ble_version: '2.3.4',
|
|
1679
|
-
},
|
|
1680
|
-
commands: { typedCall },
|
|
1681
|
-
_updateFeatures: jest.fn(),
|
|
1682
|
-
});
|
|
1683
|
-
|
|
1684
|
-
const result = await method.run();
|
|
1685
|
-
|
|
1686
|
-
expect(typedCall).toHaveBeenCalledWith(
|
|
1687
|
-
'DeviceInfoGet',
|
|
1688
|
-
'DeviceInfo',
|
|
1689
|
-
{
|
|
1690
|
-
targets: {
|
|
1691
|
-
hw: true,
|
|
1692
|
-
fw: true,
|
|
1693
|
-
coprocessor: true,
|
|
1694
|
-
},
|
|
1695
|
-
types: {
|
|
1696
|
-
version: true,
|
|
1697
|
-
specific: true,
|
|
1698
|
-
},
|
|
1699
|
-
},
|
|
1700
|
-
{ timeoutMs: PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS }
|
|
1701
|
-
);
|
|
1702
|
-
expect(result).toMatchObject({
|
|
1703
|
-
protocol: 'V2',
|
|
1704
|
-
deviceType: 'pro2',
|
|
1705
|
-
deviceId: 'PRO2-BASIC-ID',
|
|
1706
|
-
serialNo: 'PR2SERIAL',
|
|
1707
|
-
label: null,
|
|
1708
|
-
bleName: 'Raw Pro2 BLE',
|
|
1709
|
-
status: {
|
|
1710
|
-
initialized: true,
|
|
1711
|
-
unlockedAttachPin: true,
|
|
1712
|
-
passphraseProtection: true,
|
|
1713
|
-
},
|
|
1714
|
-
versions: {
|
|
1715
|
-
firmware: '5.6.7',
|
|
1716
|
-
ble: '8.9.10',
|
|
1717
|
-
},
|
|
1718
|
-
});
|
|
1719
|
-
});
|
|
1720
|
-
|
|
1721
|
-
test('does not fill Protocol V2 DeviceProfile from cached V1-shaped fields', async () => {
|
|
1722
|
-
const typedCall = jest.fn().mockResolvedValueOnce({
|
|
1723
|
-
type: 'DeviceInfo',
|
|
1724
|
-
message: {
|
|
1725
|
-
status: {
|
|
1726
|
-
init_states: true,
|
|
1727
|
-
},
|
|
1728
|
-
},
|
|
1729
|
-
});
|
|
1730
|
-
const method = new GetDeviceInfo({
|
|
1731
|
-
id: 1,
|
|
1732
|
-
payload: {
|
|
1733
|
-
method: 'getDeviceInfo',
|
|
1734
|
-
scope: 'basic',
|
|
1735
|
-
},
|
|
1736
|
-
});
|
|
1737
|
-
method.init();
|
|
1738
|
-
(method as any).device = stubDevice({
|
|
1739
|
-
originalDescriptor: { ...descriptor, protocolType: 'V2' },
|
|
1740
|
-
features: {
|
|
1741
|
-
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
1742
|
-
device_id: 'STALE-ID',
|
|
1743
|
-
serial_no: 'STALE-SERIAL',
|
|
1744
|
-
label: 'Stale Pro2',
|
|
1745
|
-
ble_name: 'Stale BLE',
|
|
1746
|
-
passphrase_protection: true,
|
|
1747
|
-
onekey_firmware_version: '1.2.3',
|
|
1748
|
-
onekey_ble_version: '2.3.4',
|
|
1749
|
-
},
|
|
1750
|
-
commands: { typedCall },
|
|
1751
|
-
});
|
|
1752
|
-
|
|
1753
|
-
const result = await method.run();
|
|
1754
|
-
|
|
1755
|
-
expect(result).toMatchObject({
|
|
1756
|
-
protocol: 'V2',
|
|
1757
|
-
// 身份字段不得取自缓存的 V1-shaped features(STALE-ID / STALE-SERIAL),
|
|
1758
|
-
// hw.serial_no 缺失时也不回退 descriptor.path。
|
|
1759
|
-
deviceId: '',
|
|
1760
|
-
serialNo: '',
|
|
1761
|
-
label: null,
|
|
1762
|
-
bleName: null,
|
|
1763
|
-
status: {
|
|
1764
|
-
passphraseProtection: null,
|
|
1765
|
-
noBackup: null,
|
|
1766
|
-
},
|
|
1767
|
-
versions: {
|
|
1768
|
-
firmware: null,
|
|
1769
|
-
ble: null,
|
|
1770
|
-
},
|
|
1771
|
-
});
|
|
1772
|
-
});
|
|
1773
|
-
|
|
1774
|
-
test('reads full Protocol V2 device info only for verify scope', async () => {
|
|
1775
|
-
const typedCall = jest
|
|
1776
|
-
.fn()
|
|
1777
|
-
.mockResolvedValueOnce({
|
|
1778
|
-
type: 'DeviceInfo',
|
|
1779
|
-
message: { hw: { serial_no: 'PR2SERIAL' } },
|
|
1780
|
-
})
|
|
1781
|
-
.mockResolvedValueOnce({
|
|
1782
|
-
type: 'DeviceStatus',
|
|
1783
|
-
message: { init_states: true },
|
|
1784
|
-
});
|
|
1785
|
-
const method = new GetDeviceInfo({
|
|
1786
|
-
id: 1,
|
|
1787
|
-
payload: {
|
|
1788
|
-
method: 'getDeviceInfo',
|
|
1789
|
-
scope: 'verify',
|
|
1790
|
-
},
|
|
1791
|
-
});
|
|
1792
|
-
method.init();
|
|
1793
|
-
(method as any).device = stubDevice({
|
|
1794
|
-
originalDescriptor: { ...descriptor, protocolType: 'V2' },
|
|
1795
|
-
features: normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
1796
|
-
commands: { typedCall },
|
|
1797
|
-
_updateFeatures: jest.fn(),
|
|
1798
|
-
});
|
|
1799
|
-
|
|
1800
|
-
await method.run();
|
|
1801
|
-
|
|
1802
|
-
expect(typedCall).toHaveBeenCalledWith(
|
|
1803
|
-
'DeviceInfoGet',
|
|
1804
|
-
'DeviceInfo',
|
|
1805
|
-
{
|
|
1806
|
-
targets: {
|
|
1807
|
-
hw: true,
|
|
1808
|
-
fw: true,
|
|
1809
|
-
coprocessor: true,
|
|
1810
|
-
se1: true,
|
|
1811
|
-
se2: true,
|
|
1812
|
-
se3: true,
|
|
1813
|
-
se4: true,
|
|
1814
|
-
},
|
|
1815
|
-
types: {
|
|
1816
|
-
version: true,
|
|
1817
|
-
build_id: true,
|
|
1818
|
-
hash: true,
|
|
1819
|
-
specific: true,
|
|
1820
|
-
},
|
|
1821
|
-
},
|
|
1822
|
-
{ timeoutMs: PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS }
|
|
1823
|
-
);
|
|
1824
|
-
});
|
|
1825
|
-
|
|
1826
|
-
test('reads Protocol V2 SE version fields for versions scope', async () => {
|
|
1827
|
-
const typedCall = jest
|
|
1828
|
-
.fn()
|
|
1829
|
-
.mockResolvedValueOnce({
|
|
1830
|
-
type: 'DeviceInfo',
|
|
1831
|
-
message: {
|
|
1832
|
-
hw: { serial_no: 'PR2SERIAL' },
|
|
1833
|
-
se1: {
|
|
1834
|
-
application: { version: '1.0.1' },
|
|
1835
|
-
bootloader: { version: '1.0.0' },
|
|
1836
|
-
},
|
|
1837
|
-
se2: {
|
|
1838
|
-
application: { version: '2.0.1' },
|
|
1839
|
-
bootloader: { version: '2.0.0' },
|
|
1840
|
-
},
|
|
1841
|
-
},
|
|
1842
|
-
})
|
|
1843
|
-
.mockResolvedValueOnce({
|
|
1844
|
-
type: 'DeviceStatus',
|
|
1845
|
-
message: { init_states: true },
|
|
1846
|
-
});
|
|
1847
|
-
const method = new GetDeviceInfo({
|
|
1848
|
-
id: 1,
|
|
1849
|
-
payload: {
|
|
1850
|
-
method: 'getDeviceInfo',
|
|
1851
|
-
scope: 'versions',
|
|
1852
|
-
},
|
|
1853
|
-
});
|
|
1854
|
-
method.init();
|
|
1855
|
-
(method as any).device = stubDevice({
|
|
1856
|
-
originalDescriptor: { ...descriptor, protocolType: 'V2' },
|
|
1857
|
-
features: normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
1858
|
-
commands: { typedCall },
|
|
1859
|
-
});
|
|
1860
|
-
|
|
1861
|
-
const result = await method.run();
|
|
1862
|
-
|
|
1863
|
-
expect(typedCall).toHaveBeenCalledWith(
|
|
1864
|
-
'DeviceInfoGet',
|
|
1865
|
-
'DeviceInfo',
|
|
1866
|
-
{
|
|
1867
|
-
targets: {
|
|
1868
|
-
hw: true,
|
|
1869
|
-
fw: true,
|
|
1870
|
-
coprocessor: true,
|
|
1871
|
-
se1: true,
|
|
1872
|
-
se2: true,
|
|
1873
|
-
se3: true,
|
|
1874
|
-
se4: true,
|
|
1875
|
-
},
|
|
1876
|
-
types: {
|
|
1877
|
-
version: true,
|
|
1878
|
-
specific: true,
|
|
1879
|
-
},
|
|
1880
|
-
},
|
|
1881
|
-
{ timeoutMs: PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS }
|
|
1882
|
-
);
|
|
1883
|
-
expect(result.versions).toMatchObject({
|
|
1884
|
-
se01: '1.0.1',
|
|
1885
|
-
se01Boot: '1.0.0',
|
|
1886
|
-
se02: '2.0.1',
|
|
1887
|
-
se02Boot: '2.0.0',
|
|
1888
|
-
});
|
|
1889
|
-
expect(result).not.toHaveProperty('verify');
|
|
1890
|
-
});
|
|
1891
|
-
|
|
1892
1734
|
test('does not inherit Pro or Pro model fallback ranges for Protocol V2 devices', () => {
|
|
1893
1735
|
const features = normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any);
|
|
1894
1736
|
const checkedTypes: string[] = [];
|
|
@@ -2007,68 +1849,38 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2007
1849
|
});
|
|
2008
1850
|
});
|
|
2009
1851
|
|
|
2010
|
-
test('
|
|
1852
|
+
test('rejects legacy getOnekeyFeatures for Protocol V2 devices', async () => {
|
|
2011
1853
|
const method = new GetOnekeyFeatures({
|
|
2012
1854
|
id: 1,
|
|
2013
1855
|
payload: {
|
|
2014
1856
|
method: 'getOnekeyFeatures',
|
|
2015
1857
|
},
|
|
2016
1858
|
});
|
|
2017
|
-
const typedCall = jest.fn()
|
|
2018
|
-
type: 'DeviceInfo',
|
|
2019
|
-
message: {
|
|
2020
|
-
protocol_version: 1,
|
|
2021
|
-
hw: { serial_no: 'PR2SERIAL' },
|
|
2022
|
-
fw: { application: { version: '1.2.3', build_id: 'app-build' } },
|
|
2023
|
-
coprocessor: { bt_adv_name: 'Pro2 BLE' },
|
|
2024
|
-
status: { init_states: true },
|
|
2025
|
-
},
|
|
2026
|
-
});
|
|
1859
|
+
const typedCall = jest.fn();
|
|
2027
1860
|
|
|
2028
1861
|
(method as any).device = stubDevice({
|
|
2029
1862
|
originalDescriptor: { protocolType: 'V2' },
|
|
2030
1863
|
commands: { typedCall },
|
|
2031
1864
|
});
|
|
2032
1865
|
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
expect(typedCall).
|
|
2037
|
-
expect(typedCall).toHaveBeenCalledWith(
|
|
2038
|
-
'DeviceInfoGet',
|
|
2039
|
-
'DeviceInfo',
|
|
2040
|
-
expect.objectContaining({
|
|
2041
|
-
targets: expect.objectContaining({ se1: true, se2: true, se3: true, se4: true }),
|
|
2042
|
-
types: expect.objectContaining({ build_id: true, hash: true }),
|
|
2043
|
-
}),
|
|
2044
|
-
expect.anything()
|
|
2045
|
-
);
|
|
2046
|
-
expect(message).toEqual({ onekey_device_type: 'PRO2' });
|
|
2047
|
-
expect(message).not.toHaveProperty('label');
|
|
1866
|
+
await expect(method.run()).rejects.toMatchObject({
|
|
1867
|
+
errorCode: expect.any(Number),
|
|
1868
|
+
});
|
|
1869
|
+
expect(typedCall).not.toHaveBeenCalled();
|
|
2048
1870
|
});
|
|
2049
1871
|
|
|
2050
|
-
test('reuses
|
|
1872
|
+
test('initializes and reuses Protocol V2 features from DeviceInfo only', async () => {
|
|
2051
1873
|
const device = Device.fromDescriptor({
|
|
2052
1874
|
path: 'usb-path',
|
|
2053
1875
|
protocolType: 'V2',
|
|
2054
1876
|
} as any);
|
|
2055
|
-
const typedCall = jest
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
},
|
|
2063
|
-
})
|
|
2064
|
-
.mockResolvedValueOnce({
|
|
2065
|
-
type: 'DeviceStatus',
|
|
2066
|
-
message: {
|
|
2067
|
-
device_id: 'PRO2-REFRESH-ID',
|
|
2068
|
-
unlocked: true,
|
|
2069
|
-
passphrase_enabled: true,
|
|
2070
|
-
},
|
|
2071
|
-
});
|
|
1877
|
+
const typedCall = jest.fn().mockResolvedValueOnce({
|
|
1878
|
+
type: 'DeviceInfo',
|
|
1879
|
+
message: {
|
|
1880
|
+
hw: { serial_no: 'PR2SERIAL' },
|
|
1881
|
+
fw: { application: { version: '1.2.3' } },
|
|
1882
|
+
},
|
|
1883
|
+
});
|
|
2072
1884
|
|
|
2073
1885
|
(device as any).commands = { typedCall };
|
|
2074
1886
|
|
|
@@ -2076,18 +1888,17 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2076
1888
|
await device.initialize();
|
|
2077
1889
|
|
|
2078
1890
|
expect(device.features).toMatchObject({
|
|
2079
|
-
deviceId:
|
|
1891
|
+
deviceId: null,
|
|
2080
1892
|
firmwareVersion: '1.2.3',
|
|
2081
|
-
passphraseProtection:
|
|
1893
|
+
passphraseProtection: null,
|
|
2082
1894
|
label: null,
|
|
2083
1895
|
});
|
|
2084
1896
|
expect((device as any).profile).toBeUndefined();
|
|
2085
|
-
expect(device.features?.passphraseProtection).
|
|
1897
|
+
expect(device.features?.passphraseProtection).toBeNull();
|
|
2086
1898
|
expect(device.features?.label).toBeNull();
|
|
2087
1899
|
expect(device.features?.firmwareVersion).toBe('1.2.3');
|
|
2088
|
-
expect(typedCall).toHaveBeenCalledTimes(
|
|
2089
|
-
expect(typedCall).
|
|
2090
|
-
1,
|
|
1900
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
1901
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
2091
1902
|
'DeviceInfoGet',
|
|
2092
1903
|
'DeviceInfo',
|
|
2093
1904
|
{
|
|
@@ -2105,10 +1916,9 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2105
1916
|
timeoutMs: PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS,
|
|
2106
1917
|
}
|
|
2107
1918
|
);
|
|
2108
|
-
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceStatusGet', 'DeviceStatus', {});
|
|
2109
1919
|
});
|
|
2110
1920
|
|
|
2111
|
-
test('
|
|
1921
|
+
test('recognizes bootloader DeviceInfo without requesting device status', async () => {
|
|
2112
1922
|
const device = Device.fromDescriptor({
|
|
2113
1923
|
path: 'usb-path',
|
|
2114
1924
|
protocolType: 'V2',
|
|
@@ -2119,23 +1929,124 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2119
1929
|
type: 'DeviceInfo',
|
|
2120
1930
|
message: {
|
|
2121
1931
|
hw: { serial_no: 'PR2SERIAL' },
|
|
2122
|
-
fw: {
|
|
1932
|
+
fw: { bootloader: { version: '0.2.0' } },
|
|
2123
1933
|
},
|
|
2124
1934
|
})
|
|
2125
1935
|
.mockResolvedValueOnce({
|
|
2126
1936
|
type: 'DeviceStatus',
|
|
2127
|
-
message: {
|
|
2128
|
-
|
|
1937
|
+
message: {
|
|
1938
|
+
device_id: 'UNEXPECTED-SEPARATE-STATUS',
|
|
1939
|
+
},
|
|
1940
|
+
});
|
|
1941
|
+
|
|
1942
|
+
(device as any).commands = { typedCall };
|
|
1943
|
+
|
|
1944
|
+
await device.initialize();
|
|
1945
|
+
|
|
1946
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
1947
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
1948
|
+
'DeviceInfoGet',
|
|
1949
|
+
'DeviceInfo',
|
|
1950
|
+
expect.not.objectContaining({
|
|
1951
|
+
targets: expect.objectContaining({ status: true }),
|
|
1952
|
+
}),
|
|
1953
|
+
expect.anything()
|
|
1954
|
+
);
|
|
1955
|
+
expect(device.features).toMatchObject({
|
|
1956
|
+
deviceId: null,
|
|
1957
|
+
bootloaderMode: true,
|
|
1958
|
+
initialized: null,
|
|
1959
|
+
});
|
|
1960
|
+
});
|
|
1961
|
+
|
|
1962
|
+
test('does not probe DeviceStatusGet while firmware device is still in bootloader mode', async () => {
|
|
1963
|
+
const method = new FirmwareUpdateV4({
|
|
1964
|
+
id: 1,
|
|
1965
|
+
payload: {
|
|
1966
|
+
method: 'firmwareUpdateV4',
|
|
1967
|
+
},
|
|
1968
|
+
});
|
|
1969
|
+
const typedCall = jest.fn().mockImplementation((request: string) => {
|
|
1970
|
+
if (request === 'DeviceStatusGet') {
|
|
1971
|
+
throw new Error('bootloader must not receive DeviceStatusGet');
|
|
1972
|
+
}
|
|
1973
|
+
return Promise.resolve({
|
|
1974
|
+
type: 'DeviceInfo',
|
|
1975
|
+
message: {
|
|
1976
|
+
hw: { serial_no: 'PR2BOOT' },
|
|
1977
|
+
fw: { bootloader: { version: '0.2.0' } },
|
|
1978
|
+
se1: {},
|
|
1979
|
+
},
|
|
1980
|
+
});
|
|
1981
|
+
});
|
|
1982
|
+
(method as any).device = stubDevice({
|
|
1983
|
+
getCommands: () => ({ typedCall }),
|
|
1984
|
+
updateProtocolV2Features: jest.fn(),
|
|
1985
|
+
});
|
|
1986
|
+
|
|
1987
|
+
await expect((method as any).probeProtocolV2NormalMode()).resolves.toBe(false);
|
|
1988
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
1989
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
1990
|
+
'DeviceInfoGet',
|
|
1991
|
+
'DeviceInfo',
|
|
1992
|
+
expect.not.objectContaining({
|
|
1993
|
+
targets: expect.objectContaining({ status: true }),
|
|
1994
|
+
}),
|
|
1995
|
+
expect.anything()
|
|
1996
|
+
);
|
|
1997
|
+
});
|
|
1998
|
+
|
|
1999
|
+
test('does not probe DeviceStatusGet while firmware device is still in romloader mode', async () => {
|
|
2000
|
+
const method = new FirmwareUpdateV4({
|
|
2001
|
+
id: 1,
|
|
2002
|
+
payload: {
|
|
2003
|
+
method: 'firmwareUpdateV4',
|
|
2004
|
+
},
|
|
2005
|
+
});
|
|
2006
|
+
const typedCall = jest.fn().mockImplementation((request: string) => {
|
|
2007
|
+
if (request === 'DeviceStatusGet') {
|
|
2008
|
+
throw new Error('romloader must not receive DeviceStatusGet');
|
|
2009
|
+
}
|
|
2010
|
+
return Promise.resolve({
|
|
2011
|
+
type: 'DeviceInfo',
|
|
2012
|
+
message: {
|
|
2013
|
+
hw: { serial_no: 'PR2ROM' },
|
|
2014
|
+
fw: {
|
|
2015
|
+
romloader: { version: '0.1.0' },
|
|
2016
|
+
bootloader: { version: '0.2.0' },
|
|
2017
|
+
},
|
|
2018
|
+
},
|
|
2019
|
+
});
|
|
2020
|
+
});
|
|
2021
|
+
(method as any).device = stubDevice({
|
|
2022
|
+
getCommands: () => ({ typedCall }),
|
|
2023
|
+
updateProtocolV2Features: jest.fn(),
|
|
2024
|
+
});
|
|
2025
|
+
|
|
2026
|
+
await expect((method as any).probeProtocolV2NormalMode()).resolves.toBe(false);
|
|
2027
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
2028
|
+
});
|
|
2029
|
+
|
|
2030
|
+
test('refreshes Protocol V2 features without falling back to V1 GetFeatures', async () => {
|
|
2031
|
+
const device = Device.fromDescriptor({
|
|
2032
|
+
path: 'usb-path',
|
|
2033
|
+
protocolType: 'V2',
|
|
2034
|
+
} as any);
|
|
2035
|
+
const typedCall = jest
|
|
2036
|
+
.fn()
|
|
2129
2037
|
.mockResolvedValueOnce({
|
|
2130
2038
|
type: 'DeviceInfo',
|
|
2131
2039
|
message: {
|
|
2132
2040
|
hw: { serial_no: 'PR2SERIAL' },
|
|
2133
|
-
fw: { application: { version: '1.2.
|
|
2041
|
+
fw: { application: { version: '1.2.3' } },
|
|
2134
2042
|
},
|
|
2135
2043
|
})
|
|
2136
2044
|
.mockResolvedValueOnce({
|
|
2137
|
-
type: '
|
|
2138
|
-
message: {
|
|
2045
|
+
type: 'DeviceInfo',
|
|
2046
|
+
message: {
|
|
2047
|
+
hw: { serial_no: 'PR2SERIAL' },
|
|
2048
|
+
fw: { application: { version: '1.2.4' } },
|
|
2049
|
+
},
|
|
2139
2050
|
});
|
|
2140
2051
|
|
|
2141
2052
|
(device as any).commands = { typedCall };
|
|
@@ -2146,17 +2057,17 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2146
2057
|
expect(device.features).toMatchObject({
|
|
2147
2058
|
deviceId: null,
|
|
2148
2059
|
firmwareVersion: '1.2.4',
|
|
2149
|
-
passphraseProtection:
|
|
2060
|
+
passphraseProtection: null,
|
|
2150
2061
|
});
|
|
2151
2062
|
expect(features).toMatchObject({
|
|
2152
2063
|
deviceType: 'pro2',
|
|
2153
2064
|
serialNo: 'PR2SERIAL',
|
|
2154
2065
|
firmwareVersion: '1.2.4',
|
|
2155
|
-
passphraseProtection:
|
|
2066
|
+
passphraseProtection: null,
|
|
2156
2067
|
});
|
|
2157
|
-
expect(typedCall).toHaveBeenCalledTimes(
|
|
2068
|
+
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
2158
2069
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
2159
|
-
|
|
2070
|
+
2,
|
|
2160
2071
|
'DeviceInfoGet',
|
|
2161
2072
|
'DeviceInfo',
|
|
2162
2073
|
{
|
|
@@ -2231,16 +2142,6 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2231
2142
|
message: { message: 'ok' },
|
|
2232
2143
|
};
|
|
2233
2144
|
}
|
|
2234
|
-
if (requestType === 'DeviceStatusGet') {
|
|
2235
|
-
return {
|
|
2236
|
-
type: 'DeviceStatus',
|
|
2237
|
-
message: {
|
|
2238
|
-
unlocked: true,
|
|
2239
|
-
unlocked_by_attach_to_pin: true,
|
|
2240
|
-
passphrase_enabled: true,
|
|
2241
|
-
},
|
|
2242
|
-
};
|
|
2243
|
-
}
|
|
2244
2145
|
throw new Error(`Unexpected request: ${requestType}`);
|
|
2245
2146
|
});
|
|
2246
2147
|
|
|
@@ -2260,7 +2161,6 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2260
2161
|
|
|
2261
2162
|
expect(typedCall.mock.calls).toEqual([
|
|
2262
2163
|
['DeviceSessionAskPin', 'Success', undefined, { timeoutMs: 120_000 }],
|
|
2263
|
-
['DeviceStatusGet', 'DeviceStatus', {}],
|
|
2264
2164
|
]);
|
|
2265
2165
|
expect(typedCall).not.toHaveBeenCalledWith('GetAddress', 'Address', expect.anything());
|
|
2266
2166
|
expect(typedCall).not.toHaveBeenCalledWith('GetFeatures', 'Features', {});
|
|
@@ -2268,17 +2168,10 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2268
2168
|
deviceType: 'pro2',
|
|
2269
2169
|
firmwareVersion: '1.2.3',
|
|
2270
2170
|
unlocked: true,
|
|
2271
|
-
unlockedAttachPin: true,
|
|
2272
|
-
passphraseProtection: true,
|
|
2273
|
-
});
|
|
2274
|
-
expect(features.raw?.protocolV2DeviceStatus).toMatchObject({
|
|
2275
|
-
unlocked: true,
|
|
2276
|
-
unlocked_by_attach_to_pin: true,
|
|
2277
|
-
passphrase_enabled: true,
|
|
2278
2171
|
});
|
|
2279
2172
|
});
|
|
2280
2173
|
|
|
2281
|
-
test('
|
|
2174
|
+
test('preserves cached Protocol V2 passphrase state without polling status after unlock', async () => {
|
|
2282
2175
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
2283
2176
|
(device as any).features = normalizeProtocolV2Features(
|
|
2284
2177
|
{ ...descriptor, protocolType: 'V2' } as any,
|
|
@@ -2295,16 +2188,6 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2295
2188
|
message: { message: 'ok' },
|
|
2296
2189
|
};
|
|
2297
2190
|
}
|
|
2298
|
-
if (requestType === 'DeviceStatusGet') {
|
|
2299
|
-
return {
|
|
2300
|
-
type: 'DeviceStatus',
|
|
2301
|
-
message: {
|
|
2302
|
-
unlocked: true,
|
|
2303
|
-
unlocked_by_attach_to_pin: true,
|
|
2304
|
-
passphrase_enabled: true,
|
|
2305
|
-
},
|
|
2306
|
-
};
|
|
2307
|
-
}
|
|
2308
2191
|
throw new Error(`Unexpected request: ${requestType}`);
|
|
2309
2192
|
});
|
|
2310
2193
|
(device as any).commands = { typedCall };
|
|
@@ -2313,12 +2196,11 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2313
2196
|
|
|
2314
2197
|
expect(typedCall.mock.calls).toEqual([
|
|
2315
2198
|
['DeviceSessionAskPin', 'Success', undefined, { timeoutMs: 120_000 }],
|
|
2316
|
-
['DeviceStatusGet', 'DeviceStatus', {}],
|
|
2317
2199
|
]);
|
|
2318
2200
|
expect((device as any).profile).toBeUndefined();
|
|
2319
2201
|
expect(device.features?.unlocked).toBe(true);
|
|
2320
|
-
expect(device.features?.passphraseProtection).
|
|
2321
|
-
expect(device.features?.unlockedAttachPin).
|
|
2202
|
+
expect(device.features?.passphraseProtection).toBeNull();
|
|
2203
|
+
expect(device.features?.unlockedAttachPin).toBeUndefined();
|
|
2322
2204
|
});
|
|
2323
2205
|
|
|
2324
2206
|
test('maps unsupported Protocol V2 DeviceSessionAskPin to DeviceNotSupportMethod', async () => {
|
|
@@ -2934,6 +2816,29 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2934
2816
|
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(1);
|
|
2935
2817
|
});
|
|
2936
2818
|
|
|
2819
|
+
test('keeps Protocol V2 romloader active before firmware transfer', async () => {
|
|
2820
|
+
const method = new FirmwareUpdateV4({
|
|
2821
|
+
id: 1,
|
|
2822
|
+
payload: {
|
|
2823
|
+
method: 'firmwareUpdateV4',
|
|
2824
|
+
},
|
|
2825
|
+
});
|
|
2826
|
+
(method as any).device = stubDevice({
|
|
2827
|
+
originalDescriptor: { id: 'usb-id', path: 'romloader-path', protocolType: 'V2' },
|
|
2828
|
+
features: { mode: 'romloader', bootloaderMode: false, capabilities: [] },
|
|
2829
|
+
isBootloader: () => false,
|
|
2830
|
+
});
|
|
2831
|
+
(method as any).protocolV2Reboot = jest
|
|
2832
|
+
.fn()
|
|
2833
|
+
.mockRejectedValue(new Error('romloader rejected Bootloader reboot type'));
|
|
2834
|
+
method.postTipMessage = jest.fn();
|
|
2835
|
+
|
|
2836
|
+
await expect((method as any).enterProtocolV2BootloaderMode()).resolves.toBe(false);
|
|
2837
|
+
|
|
2838
|
+
expect((method as any).protocolV2Reboot).not.toHaveBeenCalled();
|
|
2839
|
+
expect(method.postTipMessage).not.toHaveBeenCalledWith('AutoRebootToBootloader');
|
|
2840
|
+
});
|
|
2841
|
+
|
|
2937
2842
|
test('polls until Protocol V2 bootloader descriptor is ready after reboot', async () => {
|
|
2938
2843
|
const method = new FirmwareUpdateV4({
|
|
2939
2844
|
id: 1,
|
|
@@ -3147,7 +3052,10 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3147
3052
|
if (name === 'DeviceInfoGet') {
|
|
3148
3053
|
return Promise.resolve({
|
|
3149
3054
|
type: 'DeviceInfo',
|
|
3150
|
-
message: {
|
|
3055
|
+
message: {
|
|
3056
|
+
hw: { serial_no: 'PR9999999999' },
|
|
3057
|
+
fw: { application: { version: '9.9.9' } },
|
|
3058
|
+
},
|
|
3151
3059
|
});
|
|
3152
3060
|
}
|
|
3153
3061
|
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
@@ -3172,9 +3080,10 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3172
3080
|
|
|
3173
3081
|
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(2);
|
|
3174
3082
|
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
3175
|
-
'
|
|
3083
|
+
'DeviceInfoGet',
|
|
3176
3084
|
'DeviceStatusGet',
|
|
3177
3085
|
'DeviceInfoGet',
|
|
3086
|
+
'DeviceStatusGet',
|
|
3178
3087
|
]);
|
|
3179
3088
|
});
|
|
3180
3089
|
|
|
@@ -3199,7 +3108,10 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3199
3108
|
if (name === 'DeviceInfoGet') {
|
|
3200
3109
|
return Promise.resolve({
|
|
3201
3110
|
type: 'DeviceInfo',
|
|
3202
|
-
message: {
|
|
3111
|
+
message: {
|
|
3112
|
+
hw: { serial_no: 'PR9999999999' },
|
|
3113
|
+
fw: { application: { version: '9.9.9' } },
|
|
3114
|
+
},
|
|
3203
3115
|
});
|
|
3204
3116
|
}
|
|
3205
3117
|
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
@@ -3224,9 +3136,10 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3224
3136
|
|
|
3225
3137
|
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(2);
|
|
3226
3138
|
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
3227
|
-
'
|
|
3139
|
+
'DeviceInfoGet',
|
|
3228
3140
|
'DeviceStatusGet',
|
|
3229
3141
|
'DeviceInfoGet',
|
|
3142
|
+
'DeviceStatusGet',
|
|
3230
3143
|
]);
|
|
3231
3144
|
});
|
|
3232
3145
|
|
|
@@ -3299,7 +3212,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3299
3212
|
expect.objectContaining({ hw: { serial_no: 'PR9999999999' } }),
|
|
3300
3213
|
expect.objectContaining({ device_id: 'PRO2-DEVICE-ID' })
|
|
3301
3214
|
);
|
|
3302
|
-
expect(typedCall.mock.calls.map(call => call[0])).toEqual(['
|
|
3215
|
+
expect(typedCall.mock.calls.map(call => call[0])).toEqual(['DeviceInfoGet', 'DeviceStatusGet']);
|
|
3303
3216
|
expect(typedCall).not.toHaveBeenCalledWith(
|
|
3304
3217
|
'DeviceFirmwareUpdateStatusGet',
|
|
3305
3218
|
expect.anything(),
|
|
@@ -4510,13 +4423,15 @@ describe('Protocol V2 reboot methods', () => {
|
|
|
4510
4423
|
},
|
|
4511
4424
|
});
|
|
4512
4425
|
method.init();
|
|
4513
|
-
|
|
4426
|
+
const device = stubDevice({ commands: { typedCall } });
|
|
4427
|
+
(method as any).device = device;
|
|
4514
4428
|
|
|
4515
4429
|
await method.run();
|
|
4516
4430
|
|
|
4517
4431
|
expect(typedCall).toHaveBeenCalledWith('DeviceReboot', 'Success', {
|
|
4518
4432
|
reboot_type: 2,
|
|
4519
4433
|
});
|
|
4434
|
+
expect(device.markProtocolV2Reboot).toHaveBeenCalledWith(DeviceRebootType.Bootloader);
|
|
4520
4435
|
});
|
|
4521
4436
|
});
|
|
4522
4437
|
|
|
@@ -4557,6 +4472,20 @@ describe('Protocol V2 protected method execution', () => {
|
|
|
4557
4472
|
expect(method.unlockPolicy).toBe('none');
|
|
4558
4473
|
});
|
|
4559
4474
|
|
|
4475
|
+
test.each([
|
|
4476
|
+
['deviceInfoGet', DeviceInfoGet],
|
|
4477
|
+
['deviceStatusGet', DeviceStatusGet],
|
|
4478
|
+
['deviceSettingsGet', DeviceSettingsGet],
|
|
4479
|
+
])('does not auto-unlock the read-only %s method', (_name, MethodClass) => {
|
|
4480
|
+
const method = new MethodClass({
|
|
4481
|
+
id: 1,
|
|
4482
|
+
payload: { method: _name },
|
|
4483
|
+
} as any);
|
|
4484
|
+
method.init();
|
|
4485
|
+
|
|
4486
|
+
expect(method.unlockPolicy).toBe('none');
|
|
4487
|
+
});
|
|
4488
|
+
|
|
4560
4489
|
test('unlocks and retries an opted-in Protocol V2 method once', async () => {
|
|
4561
4490
|
const calls: string[] = [];
|
|
4562
4491
|
const method = {
|
|
@@ -5045,9 +4974,19 @@ describe('Protocol V2 current low-level methods', () => {
|
|
|
5045
4974
|
});
|
|
5046
4975
|
});
|
|
5047
4976
|
|
|
5048
|
-
test('
|
|
4977
|
+
test('does not unlock before reading Protocol V2 device settings', () => {
|
|
4978
|
+
const method = new DeviceSettingsGet({
|
|
4979
|
+
id: 1,
|
|
4980
|
+
payload: { method: 'deviceSettingsGet' },
|
|
4981
|
+
});
|
|
4982
|
+
|
|
4983
|
+
method.init();
|
|
4984
|
+
|
|
4985
|
+
expect(method.unlockPolicy).toBe('none');
|
|
4986
|
+
});
|
|
4987
|
+
|
|
4988
|
+
test('marks Protocol V2 settings mutations for unlock-on-locked retry', () => {
|
|
5049
4989
|
const methods = [
|
|
5050
|
-
new DeviceSettingsGet({ id: 1, payload: { method: 'deviceSettingsGet' } }),
|
|
5051
4990
|
new DeviceSettingsSet({
|
|
5052
4991
|
id: 2,
|
|
5053
4992
|
payload: { method: 'deviceSettingsSet', settings: { brightness: 80 } },
|
|
@@ -5139,6 +5078,12 @@ describe('Protocol V2 raw device info method', () => {
|
|
|
5139
5078
|
});
|
|
5140
5079
|
|
|
5141
5080
|
test('rejects removed target/type names before transport call', () => {
|
|
5081
|
+
expect(() =>
|
|
5082
|
+
buildMethod({
|
|
5083
|
+
targets: { hw: true, status: true },
|
|
5084
|
+
})
|
|
5085
|
+
).toThrow('targets');
|
|
5086
|
+
|
|
5142
5087
|
expect(() =>
|
|
5143
5088
|
buildMethod({
|
|
5144
5089
|
targets: { hw: true, bt: true },
|