@onekeyfe/hd-core 1.2.0-alpha.23 → 1.2.0-alpha.24
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__/DeviceCommands.test.ts +35 -0
- package/__tests__/connectSettings.test.ts +5 -47
- package/__tests__/device-lifecycle-events.test.ts +59 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +30 -39
- package/__tests__/open-wallet-session.test.ts +192 -129
- package/__tests__/protocol-v2-ping.test.ts +28 -0
- package/__tests__/protocol-v2.test.ts +338 -376
- package/__tests__/public-pro2-api-boundary.test.ts +22 -0
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +0 -11
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts +3 -7
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +5 -21
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts +1 -8
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -7
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +0 -7
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/updateBootloader.d.ts +0 -2
- package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts +1 -9
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/Ping.d.ts +2 -0
- package/dist/api/protocol-v2/Ping.d.ts.map +1 -1
- package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +0 -1
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/index.d.ts +89 -273
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +783 -2443
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts +2 -3
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +12 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/topLevelInject.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +0 -3
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
- package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +1 -4
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +0 -69
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +4 -10
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +0 -12
- package/dist/types/api/openWalletSession.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +4 -0
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/settings.d.ts +0 -13
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +3 -23
- package/src/api/FirmwareUpdateV2.ts +120 -292
- package/src/api/FirmwareUpdateV3.ts +55 -256
- package/src/api/FirmwareUpdateV4.ts +297 -757
- package/src/api/OpenWalletSession.ts +23 -86
- package/src/api/device/DeviceUpdateBootloader.ts +6 -122
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +0 -50
- package/src/api/firmware/updateBootloader.ts +4 -19
- package/src/api/firmware/uploadFirmware.ts +22 -140
- package/src/api/index.ts +1 -0
- package/src/api/protocol-v2/Ping.ts +32 -1
- package/src/core/index.ts +39 -7
- package/src/data/messages/messages-protocol-v2.json +33 -1
- package/src/data-manager/DataManager.ts +33 -33
- package/src/data-manager/connectSettings.ts +0 -11
- package/src/device/Device.ts +7 -9
- package/src/device/DeviceCommands.ts +19 -0
- package/src/index.ts +0 -5
- package/src/inject.ts +4 -22
- package/src/lowLevelInject.ts +1 -5
- package/src/protocols/protocol-v2/unlockRetry.ts +20 -3
- package/src/protocols/protocol-v2/walletSession.ts +26 -8
- package/src/topLevelInject.ts +1 -5
- package/src/types/api/checkAllFirmwareRelease.ts +0 -3
- package/src/types/api/deviceUpdateBootloader.ts +0 -6
- package/src/types/api/export.ts +0 -18
- package/src/types/api/firmwareUpdate.ts +0 -76
- package/src/types/api/index.ts +3 -14
- package/src/types/api/openWalletSession.ts +0 -19
- package/src/types/api/protocolV2.ts +10 -0
- package/src/types/settings.ts +0 -13
- package/src/utils/deviceInfoUtils.ts +15 -3
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -86
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
- package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
- package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -326
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -213
- package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -23
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -30
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
- package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdatePlan.d.ts +0 -27
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
- package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
- package/src/api/firmware/FirmwareHostBinding.ts +0 -100
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
- package/src/api/firmware/FirmwareUpdatePlan.ts +0 -621
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -392
- package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
- package/src/types/api/firmwareUpdatePlan.ts +0 -36
- package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
2
|
import { DeviceRebootType, DeviceSessionPinType, DeviceSettingsPage } from '@onekeyfe/hd-transport';
|
|
3
3
|
|
|
4
4
|
import * as firmwareBinaryApi from '../src/api/firmware/getBinary';
|
|
@@ -83,7 +83,6 @@ import {
|
|
|
83
83
|
isMethodVersionRangeUnsupported,
|
|
84
84
|
} from '../src/utils';
|
|
85
85
|
import { getDeviceFirmwareVersion } from '../src/utils/deviceVersionUtils';
|
|
86
|
-
import { openFirmwareByteSource } from '../src/api/firmware/FirmwareArtifactSource';
|
|
87
86
|
import {
|
|
88
87
|
getPassphraseState,
|
|
89
88
|
getPassphraseStateWithRefreshDeviceInfo,
|
|
@@ -98,6 +97,14 @@ jest.mock('../src/data/config', () => ({
|
|
|
98
97
|
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
99
98
|
}));
|
|
100
99
|
|
|
100
|
+
const createWalletSessionTypedCall = (implementation = jest.fn()) =>
|
|
101
|
+
jest.fn((request: string, ...args: unknown[]) => {
|
|
102
|
+
if (request === 'ProtocolInfoRequest') {
|
|
103
|
+
return Promise.resolve({ message: { version: 2 } });
|
|
104
|
+
}
|
|
105
|
+
return implementation(request, ...args);
|
|
106
|
+
});
|
|
107
|
+
|
|
101
108
|
describe('DeviceUploadWallpaper', () => {
|
|
102
109
|
test('encodes, uploads and applies a Pro2 wallpaper', async () => {
|
|
103
110
|
const rgba = new Uint8Array(604 * 1024 * 4).fill(255);
|
|
@@ -690,13 +697,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
690
697
|
const features = normalizeProtocolV2Features(descriptor as any);
|
|
691
698
|
features.firmwareVersion = '1.2.3';
|
|
692
699
|
features.passphraseProtection = true;
|
|
693
|
-
const typedCall =
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
+
const typedCall = createWalletSessionTypedCall(
|
|
701
|
+
jest.fn().mockResolvedValue({
|
|
702
|
+
type: 'DeviceSession',
|
|
703
|
+
message: {
|
|
704
|
+
btc_test_address: 'state-1',
|
|
705
|
+
session_id: 'session-1',
|
|
706
|
+
},
|
|
707
|
+
})
|
|
708
|
+
);
|
|
700
709
|
const commands = { typedCall } as unknown as DeviceCommands;
|
|
701
710
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
702
711
|
(device as any).features = features;
|
|
@@ -724,13 +733,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
724
733
|
{ ...descriptor, protocolType: 'V2' } as any,
|
|
725
734
|
{ status: { passphrase_enabled: false, unlocked: true } }
|
|
726
735
|
);
|
|
727
|
-
const typedCall =
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
736
|
+
const typedCall = createWalletSessionTypedCall(
|
|
737
|
+
jest.fn().mockResolvedValue({
|
|
738
|
+
type: 'DeviceSession',
|
|
739
|
+
message: {
|
|
740
|
+
btc_test_address: 'main-wallet-state',
|
|
741
|
+
session_id: 'main-wallet-session',
|
|
742
|
+
},
|
|
743
|
+
})
|
|
744
|
+
);
|
|
734
745
|
const promptPassphrase = jest
|
|
735
746
|
.fn()
|
|
736
747
|
.mockResolvedValue({ passphrase: 'hidden-wallet-with-disabled-flag' });
|
|
@@ -800,17 +811,16 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
800
811
|
},
|
|
801
812
|
}
|
|
802
813
|
);
|
|
803
|
-
const typedCall =
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
});
|
|
814
|
+
const typedCall = createWalletSessionTypedCall(
|
|
815
|
+
jest.fn().mockImplementation((request: string) => {
|
|
816
|
+
if (request === 'DeviceStatusGet') {
|
|
817
|
+
return Promise.resolve({
|
|
818
|
+
message: { passphrase_enabled: false, unlocked: true },
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
822
|
+
})
|
|
823
|
+
);
|
|
814
824
|
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'hidden-after-unlock' });
|
|
815
825
|
const unlockDevice = jest.spyOn(device, 'unlockDevice').mockImplementation(() => {
|
|
816
826
|
(device as any).features = {
|
|
@@ -844,13 +854,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
844
854
|
);
|
|
845
855
|
device.passphraseState = 'hidden-state';
|
|
846
856
|
preloadSessionCache('main-wallet-device', 'hidden-state', 'hidden-session');
|
|
847
|
-
const typedCall =
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
857
|
+
const typedCall = createWalletSessionTypedCall(
|
|
858
|
+
jest.fn().mockResolvedValue({
|
|
859
|
+
type: 'DeviceSession',
|
|
860
|
+
message: {
|
|
861
|
+
btc_test_address: 'main-wallet-state',
|
|
862
|
+
session_id: 'main-wallet-session',
|
|
863
|
+
},
|
|
864
|
+
})
|
|
865
|
+
);
|
|
854
866
|
(device as any).commands = { typedCall };
|
|
855
867
|
|
|
856
868
|
await expect(
|
|
@@ -885,11 +897,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
885
897
|
},
|
|
886
898
|
}
|
|
887
899
|
);
|
|
888
|
-
const typedCall =
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
900
|
+
const typedCall = createWalletSessionTypedCall(
|
|
901
|
+
jest.fn().mockResolvedValue({
|
|
902
|
+
type: 'DeviceSession',
|
|
903
|
+
message: { session_id: 'hidden-session', btc_test_address: 'hidden-state' },
|
|
904
|
+
})
|
|
905
|
+
);
|
|
906
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphraseOnDevice: true });
|
|
893
907
|
(device as any).commands = { typedCall, promptPassphrase };
|
|
894
908
|
|
|
895
909
|
await expect(getProtocolV2WalletSession(device)).resolves.toMatchObject({
|
|
@@ -898,14 +912,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
898
912
|
});
|
|
899
913
|
|
|
900
914
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
901
|
-
expect(typedCall).toHaveBeenCalledWith(
|
|
902
|
-
'DeviceSessionAskPassphrase',
|
|
903
|
-
'Success',
|
|
904
|
-
{},
|
|
905
|
-
{
|
|
906
|
-
timeoutMs: 120_000,
|
|
907
|
-
}
|
|
908
|
-
);
|
|
915
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {});
|
|
909
916
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
910
917
|
});
|
|
911
918
|
|
|
@@ -999,10 +1006,12 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
999
1006
|
);
|
|
1000
1007
|
device.passphraseState = 'state-a';
|
|
1001
1008
|
preloadSessionCache('stable-device-1', 'state-a', 'session-a');
|
|
1002
|
-
const typedCall =
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1009
|
+
const typedCall = createWalletSessionTypedCall(
|
|
1010
|
+
jest.fn().mockResolvedValue({
|
|
1011
|
+
type: 'DeviceSession',
|
|
1012
|
+
message: { session_id: 'session-b', btc_test_address: 'state-a' },
|
|
1013
|
+
})
|
|
1014
|
+
);
|
|
1006
1015
|
(device as any).commands = { typedCall };
|
|
1007
1016
|
|
|
1008
1017
|
await expect(getProtocolV2WalletSession(device)).resolves.toMatchObject({
|
|
@@ -1027,13 +1036,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1027
1036
|
{ status: { device_id: 'stable-device-2', unlocked: true } }
|
|
1028
1037
|
);
|
|
1029
1038
|
preloadSessionCache('stable-device-2', 'state-a', 'session-a');
|
|
1030
|
-
const typedCall =
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1039
|
+
const typedCall = createWalletSessionTypedCall(
|
|
1040
|
+
jest.fn().mockResolvedValue({
|
|
1041
|
+
type: 'DeviceSession',
|
|
1042
|
+
message: {
|
|
1043
|
+
session_id: 'main-session',
|
|
1044
|
+
btc_test_address: 'main-wallet-state',
|
|
1045
|
+
},
|
|
1046
|
+
})
|
|
1047
|
+
);
|
|
1037
1048
|
(device as any).commands = { typedCall };
|
|
1038
1049
|
|
|
1039
1050
|
await getProtocolV2WalletSession(device, { onlyMainPin: true });
|
|
@@ -1062,14 +1073,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1062
1073
|
);
|
|
1063
1074
|
device.passphraseState = 'state-a';
|
|
1064
1075
|
preloadSessionCache('stable-device-3', 'state-a', 'session-a');
|
|
1065
|
-
const typedCall =
|
|
1066
|
-
.fn()
|
|
1067
|
-
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1068
|
-
.mockRejectedValue(
|
|
1076
|
+
const typedCall = createWalletSessionTypedCall(
|
|
1077
|
+
jest.fn().mockRejectedValue(
|
|
1069
1078
|
Object.assign(new Error('SE request failed'), {
|
|
1070
1079
|
errorCode: HardwareErrorCode.RuntimeError,
|
|
1071
1080
|
})
|
|
1072
|
-
)
|
|
1081
|
+
)
|
|
1082
|
+
);
|
|
1073
1083
|
const promptPassphrase = jest.fn();
|
|
1074
1084
|
(device as any).commands = { typedCall, promptPassphrase };
|
|
1075
1085
|
|
|
@@ -1100,17 +1110,16 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1100
1110
|
},
|
|
1101
1111
|
}
|
|
1102
1112
|
);
|
|
1103
|
-
const typedCall =
|
|
1104
|
-
.fn()
|
|
1105
|
-
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1106
|
-
.mockRejectedValue(
|
|
1113
|
+
const typedCall = createWalletSessionTypedCall(
|
|
1114
|
+
jest.fn().mockRejectedValue(
|
|
1107
1115
|
Object.assign(new Error('Invalid session'), {
|
|
1108
1116
|
errorCode: HardwareErrorCode.WalletSessionInvalid,
|
|
1109
1117
|
})
|
|
1110
|
-
)
|
|
1118
|
+
)
|
|
1119
|
+
);
|
|
1111
1120
|
(device as any).commands = {
|
|
1112
1121
|
typedCall,
|
|
1113
|
-
promptPassphrase: jest.fn().mockResolvedValue({
|
|
1122
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphraseOnDevice: true }),
|
|
1114
1123
|
};
|
|
1115
1124
|
|
|
1116
1125
|
await expect(getProtocolV2WalletSession(device)).rejects.toMatchObject({
|
|
@@ -1118,7 +1127,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1118
1127
|
});
|
|
1119
1128
|
expect(typedCall.mock.calls).toEqual([
|
|
1120
1129
|
['ProtocolInfoRequest', 'ProtocolInfo', { eventless_wallet_session: true }],
|
|
1121
|
-
['DeviceSessionAskPassphrase', 'Success', {}
|
|
1130
|
+
['DeviceSessionAskPassphrase', 'Success', {}],
|
|
1122
1131
|
]);
|
|
1123
1132
|
});
|
|
1124
1133
|
|
|
@@ -1141,10 +1150,12 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1141
1150
|
device.passphraseState = 'state-a';
|
|
1142
1151
|
preloadSessionCache('stable-device-mismatch', 'state-a', 'session-a');
|
|
1143
1152
|
(device as any).commands = {
|
|
1144
|
-
typedCall:
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1153
|
+
typedCall: createWalletSessionTypedCall(
|
|
1154
|
+
jest.fn().mockResolvedValue({
|
|
1155
|
+
type: 'DeviceSession',
|
|
1156
|
+
message: { session_id: 'session-b', btc_test_address: 'state-b' },
|
|
1157
|
+
})
|
|
1158
|
+
),
|
|
1148
1159
|
};
|
|
1149
1160
|
|
|
1150
1161
|
await expect(
|
|
@@ -1170,20 +1181,21 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1170
1181
|
const lockedError = Object.assign(new Error('Device is locked'), {
|
|
1171
1182
|
errorCode: HardwareErrorCode.DeviceLocked,
|
|
1172
1183
|
});
|
|
1173
|
-
const typedCall =
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
+
const typedCall = createWalletSessionTypedCall(
|
|
1185
|
+
jest
|
|
1186
|
+
.fn()
|
|
1187
|
+
.mockRejectedValueOnce(lockedError)
|
|
1188
|
+
.mockResolvedValueOnce({ message: { message: 'passphrase prepared' } })
|
|
1189
|
+
.mockResolvedValueOnce({
|
|
1190
|
+
message: {
|
|
1191
|
+
session_id: 'session-after-unlock',
|
|
1192
|
+
btc_test_address: 'state-after-unlock',
|
|
1193
|
+
},
|
|
1194
|
+
})
|
|
1195
|
+
);
|
|
1184
1196
|
(device as any).commands = {
|
|
1185
1197
|
typedCall,
|
|
1186
|
-
promptPassphrase: jest.fn().mockResolvedValue({
|
|
1198
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphraseOnDevice: true }),
|
|
1187
1199
|
};
|
|
1188
1200
|
const unlockDevice = jest.spyOn(device, 'unlockDevice').mockResolvedValue(undefined as any);
|
|
1189
1201
|
|
|
@@ -1335,7 +1347,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1335
1347
|
};
|
|
1336
1348
|
const typedCall = jest
|
|
1337
1349
|
.fn()
|
|
1338
|
-
.mockResolvedValueOnce({ message: {
|
|
1350
|
+
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1339
1351
|
.mockResolvedValueOnce({ message: { message: 'passphrase prepared' } })
|
|
1340
1352
|
.mockResolvedValueOnce({
|
|
1341
1353
|
type: 'DeviceSession',
|
|
@@ -1346,7 +1358,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1346
1358
|
});
|
|
1347
1359
|
const updateInternalState = jest.fn();
|
|
1348
1360
|
const getFeatures = jest.fn().mockResolvedValue(features);
|
|
1349
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
1361
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphraseOnDevice: true });
|
|
1350
1362
|
const method = new GetPassphraseState({
|
|
1351
1363
|
payload: {
|
|
1352
1364
|
method: 'getPassphraseState',
|
|
@@ -1370,8 +1382,12 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1370
1382
|
|
|
1371
1383
|
await expect(method.run()).resolves.toBe('state-pro2');
|
|
1372
1384
|
expect(promptPassphrase).toHaveBeenCalledWith(
|
|
1373
|
-
|
|
1374
|
-
|
|
1385
|
+
{
|
|
1386
|
+
existsAttachPinUser: false,
|
|
1387
|
+
source: 'wallet-session-coordinator',
|
|
1388
|
+
reason: 'open-wallet',
|
|
1389
|
+
},
|
|
1390
|
+
{ cancelDeviceOnReject: false }
|
|
1375
1391
|
);
|
|
1376
1392
|
expect(updateInternalState).toHaveBeenCalledWith(
|
|
1377
1393
|
true,
|
|
@@ -1388,7 +1404,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1388
1404
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
1389
1405
|
const typedCall = jest.fn().mockImplementation((request: string) => {
|
|
1390
1406
|
if (request === 'ProtocolInfoRequest') {
|
|
1391
|
-
return Promise.resolve({ message: {
|
|
1407
|
+
return Promise.resolve({ message: { version: 2 } });
|
|
1392
1408
|
}
|
|
1393
1409
|
if (request === 'DeviceSessionAskPassphrase') {
|
|
1394
1410
|
return Promise.resolve({ message: { message: 'passphrase prepared' } });
|
|
@@ -1451,7 +1467,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1451
1467
|
passphraseProtection: false,
|
|
1452
1468
|
unlocked: true,
|
|
1453
1469
|
};
|
|
1454
|
-
const typedCall =
|
|
1470
|
+
const typedCall = createWalletSessionTypedCall();
|
|
1455
1471
|
const updateInternalState = jest.fn();
|
|
1456
1472
|
const method = new GetPassphraseState({
|
|
1457
1473
|
payload: {
|
|
@@ -1465,7 +1481,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1465
1481
|
features,
|
|
1466
1482
|
commands: {
|
|
1467
1483
|
typedCall,
|
|
1468
|
-
promptPassphrase: jest.fn().mockResolvedValue({
|
|
1484
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphraseOnDevice: true }),
|
|
1469
1485
|
},
|
|
1470
1486
|
updateInternalState,
|
|
1471
1487
|
getCurrentDeviceType: () => 'pro2',
|
|
@@ -1491,7 +1507,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1491
1507
|
};
|
|
1492
1508
|
const typedCall = jest
|
|
1493
1509
|
.fn()
|
|
1494
|
-
.mockResolvedValueOnce({ message: {
|
|
1510
|
+
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1495
1511
|
.mockResolvedValueOnce({ message: { message: 'passphrase prepared' } })
|
|
1496
1512
|
.mockResolvedValueOnce({
|
|
1497
1513
|
type: 'DeviceSession',
|
|
@@ -1515,7 +1531,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1515
1531
|
passphraseState: 'state-pro2-old',
|
|
1516
1532
|
commands: {
|
|
1517
1533
|
typedCall,
|
|
1518
|
-
promptPassphrase: jest.fn().mockResolvedValue({
|
|
1534
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphraseOnDevice: true }),
|
|
1519
1535
|
},
|
|
1520
1536
|
emit: jest.fn(),
|
|
1521
1537
|
clearInternalState,
|
|
@@ -1538,13 +1554,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1538
1554
|
|
|
1539
1555
|
test('stores Pro2 passphrase session cache without synthetic device id', async () => {
|
|
1540
1556
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
1541
|
-
const typedCall =
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1557
|
+
const typedCall = createWalletSessionTypedCall(
|
|
1558
|
+
jest.fn().mockResolvedValue({
|
|
1559
|
+
type: 'DeviceSession',
|
|
1560
|
+
message: {
|
|
1561
|
+
btc_test_address: 'state-profile',
|
|
1562
|
+
session_id: 'session-profile',
|
|
1563
|
+
},
|
|
1564
|
+
})
|
|
1565
|
+
);
|
|
1548
1566
|
|
|
1549
1567
|
(device as any).features = {
|
|
1550
1568
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, {
|
|
@@ -1556,7 +1574,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1556
1574
|
};
|
|
1557
1575
|
(device as any).commands = {
|
|
1558
1576
|
typedCall,
|
|
1559
|
-
promptPassphrase: jest.fn().mockResolvedValue({
|
|
1577
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphraseOnDevice: true }),
|
|
1560
1578
|
};
|
|
1561
1579
|
|
|
1562
1580
|
await getPassphraseStateWithRefreshDeviceInfo(device);
|
|
@@ -1568,13 +1586,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1568
1586
|
|
|
1569
1587
|
test('stores Pro2 passphrase sessions without selecting them implicitly', async () => {
|
|
1570
1588
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
1571
|
-
const typedCall =
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1589
|
+
const typedCall = createWalletSessionTypedCall(
|
|
1590
|
+
jest.fn().mockResolvedValue({
|
|
1591
|
+
type: 'DeviceSession',
|
|
1592
|
+
message: {
|
|
1593
|
+
btc_test_address: 'state-auto',
|
|
1594
|
+
session_id: 'session-auto',
|
|
1595
|
+
},
|
|
1596
|
+
})
|
|
1597
|
+
);
|
|
1578
1598
|
|
|
1579
1599
|
(device as any).features = normalizeProtocolV2Features(
|
|
1580
1600
|
{
|
|
@@ -1594,7 +1614,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1594
1614
|
);
|
|
1595
1615
|
(device as any).commands = {
|
|
1596
1616
|
typedCall,
|
|
1597
|
-
promptPassphrase: jest.fn().mockResolvedValue({
|
|
1617
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphraseOnDevice: true }),
|
|
1598
1618
|
};
|
|
1599
1619
|
|
|
1600
1620
|
await expect(getPassphraseStateWithRefreshDeviceInfo(device)).resolves.toMatchObject({
|
|
@@ -1613,13 +1633,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1613
1633
|
|
|
1614
1634
|
test('does not mark Pro2 passphrase enabled from a main PIN session alone', async () => {
|
|
1615
1635
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
1616
|
-
const typedCall =
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1636
|
+
const typedCall = createWalletSessionTypedCall(
|
|
1637
|
+
jest.fn().mockResolvedValue({
|
|
1638
|
+
type: 'DeviceSession',
|
|
1639
|
+
message: {
|
|
1640
|
+
session_id: 'main-pin-session',
|
|
1641
|
+
btc_test_address: 'main-wallet-state',
|
|
1642
|
+
},
|
|
1643
|
+
})
|
|
1644
|
+
);
|
|
1623
1645
|
|
|
1624
1646
|
(device as any).features = normalizeProtocolV2Features(
|
|
1625
1647
|
{
|
|
@@ -1654,13 +1676,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1654
1676
|
|
|
1655
1677
|
test('does not let skipPassphraseCheck hide Pro2 passphrase state mismatch', async () => {
|
|
1656
1678
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
1657
|
-
const typedCall =
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1679
|
+
const typedCall = createWalletSessionTypedCall(
|
|
1680
|
+
jest.fn().mockResolvedValue({
|
|
1681
|
+
type: 'DeviceSession',
|
|
1682
|
+
message: {
|
|
1683
|
+
btc_test_address: 'wrong-state',
|
|
1684
|
+
session_id: 'wrong-session',
|
|
1685
|
+
},
|
|
1686
|
+
})
|
|
1687
|
+
);
|
|
1664
1688
|
|
|
1665
1689
|
(device as any).features = normalizeProtocolV2Features({
|
|
1666
1690
|
...descriptor,
|
|
@@ -1671,7 +1695,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1671
1695
|
(device as any).features.unlocked = true;
|
|
1672
1696
|
(device as any).commands = {
|
|
1673
1697
|
typedCall,
|
|
1674
|
-
promptPassphrase: jest.fn().mockResolvedValue({
|
|
1698
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphraseOnDevice: true }),
|
|
1675
1699
|
};
|
|
1676
1700
|
|
|
1677
1701
|
await expect(device.checkPassphraseStateSafety('expected-state', false, true)).rejects.toEqual(
|
|
@@ -1681,26 +1705,20 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1681
1705
|
);
|
|
1682
1706
|
|
|
1683
1707
|
expect(device.getInternalState()).toBeUndefined();
|
|
1684
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1685
|
-
2,
|
|
1686
|
-
'DeviceSessionAskPassphrase',
|
|
1687
|
-
'Success',
|
|
1688
|
-
{},
|
|
1689
|
-
{
|
|
1690
|
-
timeoutMs: 120_000,
|
|
1691
|
-
}
|
|
1692
|
-
);
|
|
1708
|
+
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {});
|
|
1693
1709
|
});
|
|
1694
1710
|
|
|
1695
1711
|
test('useEmptyPassphrase ignores a stale hidden-wallet state during Protocol V2 safety check', async () => {
|
|
1696
1712
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
1697
|
-
const typedCall =
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1713
|
+
const typedCall = createWalletSessionTypedCall(
|
|
1714
|
+
jest.fn().mockResolvedValue({
|
|
1715
|
+
type: 'DeviceSession',
|
|
1716
|
+
message: {
|
|
1717
|
+
session_id: 'main-wallet-session',
|
|
1718
|
+
btc_test_address: 'main-wallet-state',
|
|
1719
|
+
},
|
|
1720
|
+
})
|
|
1721
|
+
);
|
|
1704
1722
|
|
|
1705
1723
|
(device as any).features = normalizeProtocolV2Features(
|
|
1706
1724
|
{ ...descriptor, protocolType: 'V2' } as any,
|
|
@@ -1800,7 +1818,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1800
1818
|
expect(device.hasUsePassphrase()).toBe(true);
|
|
1801
1819
|
});
|
|
1802
1820
|
|
|
1803
|
-
test('
|
|
1821
|
+
test('uses the BLE name as the Protocol V2 display label while preserving the raw label', () => {
|
|
1804
1822
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
1805
1823
|
(device as any).features = {
|
|
1806
1824
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
@@ -1813,8 +1831,9 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1813
1831
|
serialNo: 'PR9999999999',
|
|
1814
1832
|
bleName: 'Pro2 6136',
|
|
1815
1833
|
name: 'Pro2 6136',
|
|
1816
|
-
label: '',
|
|
1834
|
+
label: 'Pro2 6136',
|
|
1817
1835
|
});
|
|
1836
|
+
expect(device.features?.label).toBeNull();
|
|
1818
1837
|
});
|
|
1819
1838
|
|
|
1820
1839
|
test('prefers the real Protocol V2 label over the BLE name for display', () => {
|
|
@@ -2333,12 +2352,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2333
2352
|
const features = await device.unlockDevice();
|
|
2334
2353
|
|
|
2335
2354
|
expect(typedCall.mock.calls).toEqual([
|
|
2336
|
-
[
|
|
2337
|
-
'DeviceSessionAskPin',
|
|
2338
|
-
'Success',
|
|
2339
|
-
{ type: DeviceSessionPinType.Main },
|
|
2340
|
-
{ timeoutMs: 120_000 },
|
|
2341
|
-
],
|
|
2355
|
+
['DeviceSessionAskPin', 'Success', { type: DeviceSessionPinType.Main }],
|
|
2342
2356
|
['DeviceStatusGet', 'DeviceStatus', {}],
|
|
2343
2357
|
]);
|
|
2344
2358
|
expect(typedCall).not.toHaveBeenCalledWith('GetAddress', 'Address', expect.anything());
|
|
@@ -2386,12 +2400,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2386
2400
|
await device.unlockDevice();
|
|
2387
2401
|
|
|
2388
2402
|
expect(typedCall.mock.calls).toEqual([
|
|
2389
|
-
[
|
|
2390
|
-
'DeviceSessionAskPin',
|
|
2391
|
-
'Success',
|
|
2392
|
-
{ type: DeviceSessionPinType.Main },
|
|
2393
|
-
{ timeoutMs: 120_000 },
|
|
2394
|
-
],
|
|
2403
|
+
['DeviceSessionAskPin', 'Success', { type: DeviceSessionPinType.Main }],
|
|
2395
2404
|
['DeviceStatusGet', 'DeviceStatus', {}],
|
|
2396
2405
|
]);
|
|
2397
2406
|
expect((device as any).profile).toBeUndefined();
|
|
@@ -2421,12 +2430,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2421
2430
|
);
|
|
2422
2431
|
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
2423
2432
|
expect(typedCall.mock.calls).toEqual([
|
|
2424
|
-
[
|
|
2425
|
-
'DeviceSessionAskPin',
|
|
2426
|
-
'Success',
|
|
2427
|
-
{ type: DeviceSessionPinType.Main },
|
|
2428
|
-
{ timeoutMs: 120_000 },
|
|
2429
|
-
],
|
|
2433
|
+
['DeviceSessionAskPin', 'Success', { type: DeviceSessionPinType.Main }],
|
|
2430
2434
|
]);
|
|
2431
2435
|
expect(typedCall).not.toHaveBeenCalledWith(
|
|
2432
2436
|
'DeviceSessionGet',
|
|
@@ -2942,7 +2946,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2942
2946
|
});
|
|
2943
2947
|
});
|
|
2944
2948
|
|
|
2945
|
-
test('
|
|
2949
|
+
test('enters Protocol V2 bootloader before upload', async () => {
|
|
2946
2950
|
const method = new FirmwareUpdateV4({
|
|
2947
2951
|
id: 1,
|
|
2948
2952
|
payload: {
|
|
@@ -2977,7 +2981,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2977
2981
|
|
|
2978
2982
|
await method.run();
|
|
2979
2983
|
|
|
2980
|
-
expect((method as any).enterProtocolV2BootloaderMode).
|
|
2984
|
+
expect((method as any).enterProtocolV2BootloaderMode).toHaveBeenCalledTimes(1);
|
|
2981
2985
|
expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
|
|
2982
2986
|
fwBinaryMap: [
|
|
2983
2987
|
{
|
|
@@ -3296,9 +3300,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3296
3300
|
const typedCall = jest
|
|
3297
3301
|
.fn()
|
|
3298
3302
|
.mockRejectedValue(
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3303
|
+
new DOMException(
|
|
3304
|
+
"Failed to execute 'open' on 'USBDevice': The device was disconnected",
|
|
3305
|
+
'NotFoundError'
|
|
3302
3306
|
)
|
|
3303
3307
|
);
|
|
3304
3308
|
|
|
@@ -3386,7 +3390,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3386
3390
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
|
|
3387
3391
|
});
|
|
3388
3392
|
|
|
3389
|
-
test('
|
|
3393
|
+
test('accepts a missing firmware status handler as an app reboot signal', async () => {
|
|
3390
3394
|
const method = new FirmwareUpdateV4({
|
|
3391
3395
|
id: 1,
|
|
3392
3396
|
payload: {
|
|
@@ -3405,62 +3409,12 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3405
3409
|
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(undefined);
|
|
3406
3410
|
method.postProgressMessage = jest.fn();
|
|
3407
3411
|
|
|
3408
|
-
await
|
|
3409
|
-
|
|
3410
|
-
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
3411
|
-
])
|
|
3412
|
-
).rejects.toThrow('Protocol V2 firmware install status is unavailable');
|
|
3413
|
-
|
|
3414
|
-
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(1);
|
|
3415
|
-
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
3416
|
-
'DeviceFirmwareUpdateStatusGet',
|
|
3417
|
-
'DeviceInfoGet',
|
|
3412
|
+
await (method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
3413
|
+
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
3418
3414
|
]);
|
|
3419
|
-
});
|
|
3420
3415
|
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
id: 1,
|
|
3424
|
-
payload: {
|
|
3425
|
-
method: 'firmwareUpdateV4',
|
|
3426
|
-
platform: 'desktop',
|
|
3427
|
-
expectedTargetVersions: {
|
|
3428
|
-
app_v1: '2.0.0',
|
|
3429
|
-
},
|
|
3430
|
-
},
|
|
3431
|
-
});
|
|
3432
|
-
method.init();
|
|
3433
|
-
method.postProgressMessage = jest.fn();
|
|
3434
|
-
|
|
3435
|
-
expect(() =>
|
|
3436
|
-
(method as any).assertProtocolV2TargetStatus(
|
|
3437
|
-
[{ target_id: 4, status: 2, payload_version: 0x010000 }],
|
|
3438
|
-
new Set([4])
|
|
3439
|
-
)
|
|
3440
|
-
).toThrow('expected 131072');
|
|
3441
|
-
});
|
|
3442
|
-
|
|
3443
|
-
test('does not fail a completed multi-app install when final app versions are unobservable', () => {
|
|
3444
|
-
const method = new FirmwareUpdateV4({
|
|
3445
|
-
id: 1,
|
|
3446
|
-
payload: {
|
|
3447
|
-
method: 'firmwareUpdateV4',
|
|
3448
|
-
platform: 'desktop',
|
|
3449
|
-
targetsToUpdate: ['app_v1', 'app_v2'],
|
|
3450
|
-
expectedTargetVersions: {
|
|
3451
|
-
app_v1: '1.0.0',
|
|
3452
|
-
app_v2: '2.0.0',
|
|
3453
|
-
},
|
|
3454
|
-
},
|
|
3455
|
-
});
|
|
3456
|
-
method.init();
|
|
3457
|
-
(method as any).protocolV2LatestFinalFeatures = {
|
|
3458
|
-
major_version: 3,
|
|
3459
|
-
minor_version: 0,
|
|
3460
|
-
patch_version: 0,
|
|
3461
|
-
};
|
|
3462
|
-
|
|
3463
|
-
expect(() => (method as any).assertExpectedProtocolV2Versions()).not.toThrow();
|
|
3416
|
+
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(1);
|
|
3417
|
+
expect(typedCall.mock.calls.map(call => call[0])).toEqual(['DeviceFirmwareUpdateStatusGet']);
|
|
3464
3418
|
});
|
|
3465
3419
|
|
|
3466
3420
|
test('polls target status after update ACK and finishes when all targets complete', async () => {
|
|
@@ -3639,14 +3593,10 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3639
3593
|
const writtenPaths: string[] = [];
|
|
3640
3594
|
method.postTipMessage = jest.fn();
|
|
3641
3595
|
method.postProgressMessage = jest.fn();
|
|
3642
|
-
(method as any).
|
|
3596
|
+
(method as any).protocolV2CommonUpdateProcess = jest.fn().mockImplementation(params => {
|
|
3643
3597
|
writtenPaths.push(params.filePath);
|
|
3644
|
-
return Number(params.processedSize ?? 0) + Number(params.
|
|
3598
|
+
return Number(params.processedSize ?? 0) + Number(params.payload.byteLength);
|
|
3645
3599
|
});
|
|
3646
|
-
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
3647
|
-
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
3648
|
-
(method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({});
|
|
3649
|
-
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
3650
3600
|
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
3651
3601
|
(method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
|
|
3652
3602
|
(method as any).waitForProtocolV2FirmwareUpdateComplete = jest
|
|
@@ -3686,12 +3636,10 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3686
3636
|
'vol0:/coprocessor.bin',
|
|
3687
3637
|
1
|
|
3688
3638
|
);
|
|
3689
|
-
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledTimes(
|
|
3690
|
-
expect((method as any).protocolV2StartFirmwareUpdate).
|
|
3691
|
-
targets: [{ target_id: 3, path: 'vol0:/bootloader.bin' }],
|
|
3692
|
-
});
|
|
3693
|
-
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(2, {
|
|
3639
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledTimes(1);
|
|
3640
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledWith({
|
|
3694
3641
|
targets: [
|
|
3642
|
+
{ target_id: 3, path: 'vol0:/bootloader.bin' },
|
|
3695
3643
|
{ target_id: 6, path: 'vol0:/coprocessor.bin' },
|
|
3696
3644
|
{ target_id: 7, path: 'vol0:/se01.bin' },
|
|
3697
3645
|
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
@@ -3701,7 +3649,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3701
3649
|
expect((method as any).waitForProtocolV2FirmwareUpdateComplete).toHaveBeenCalled();
|
|
3702
3650
|
});
|
|
3703
3651
|
|
|
3704
|
-
test('announces
|
|
3652
|
+
test('announces one transfer when syncing resource bundles and staging firmware', async () => {
|
|
3705
3653
|
const method = new FirmwareUpdateV4({
|
|
3706
3654
|
id: 1,
|
|
3707
3655
|
payload: {
|
|
@@ -3711,14 +3659,11 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3711
3659
|
|
|
3712
3660
|
method.postTipMessage = jest.fn();
|
|
3713
3661
|
method.postProgressMessage = jest.fn();
|
|
3714
|
-
(method as any).
|
|
3662
|
+
(method as any).protocolV2CommonUpdateProcess = jest
|
|
3715
3663
|
.fn()
|
|
3716
3664
|
.mockImplementation(params =>
|
|
3717
|
-
Promise.resolve(Number(params.processedSize ?? 0) + Number(params.
|
|
3665
|
+
Promise.resolve(Number(params.processedSize ?? 0) + Number(params.payload.byteLength))
|
|
3718
3666
|
);
|
|
3719
|
-
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
3720
|
-
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
3721
|
-
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
3722
3667
|
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
3723
3668
|
(method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
|
|
3724
3669
|
(method as any).waitForProtocolV2FirmwareUpdateComplete = jest
|
|
@@ -3738,22 +3683,21 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3738
3683
|
{
|
|
3739
3684
|
fileName: 'application_p1.bin',
|
|
3740
3685
|
binary: new Uint8Array([3]).buffer,
|
|
3741
|
-
targetId:
|
|
3686
|
+
targetId: 3,
|
|
3742
3687
|
},
|
|
3743
3688
|
],
|
|
3744
3689
|
});
|
|
3745
3690
|
|
|
3746
|
-
expect(method.postTipMessage).toHaveBeenCalledTimes(
|
|
3691
|
+
expect(method.postTipMessage).toHaveBeenCalledTimes(2);
|
|
3747
3692
|
expect(method.postTipMessage).toHaveBeenNthCalledWith(1, 'StartTransferData');
|
|
3748
|
-
expect(method.postTipMessage).toHaveBeenNthCalledWith(2, '
|
|
3749
|
-
expect(method.
|
|
3750
|
-
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenNthCalledWith(
|
|
3693
|
+
expect(method.postTipMessage).toHaveBeenNthCalledWith(2, 'ConfirmOnDevice');
|
|
3694
|
+
expect((method as any).protocolV2CommonUpdateProcess).toHaveBeenNthCalledWith(
|
|
3751
3695
|
1,
|
|
3752
|
-
expect.objectContaining({ processedSize: 0, totalSize:
|
|
3696
|
+
expect.objectContaining({ processedSize: 0, totalSize: 3 })
|
|
3753
3697
|
);
|
|
3754
|
-
expect((method as any).
|
|
3698
|
+
expect((method as any).protocolV2CommonUpdateProcess).toHaveBeenNthCalledWith(
|
|
3755
3699
|
2,
|
|
3756
|
-
expect.objectContaining({ processedSize:
|
|
3700
|
+
expect.objectContaining({ processedSize: 2, totalSize: 3 })
|
|
3757
3701
|
);
|
|
3758
3702
|
});
|
|
3759
3703
|
|
|
@@ -3767,8 +3711,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3767
3711
|
|
|
3768
3712
|
method.postTipMessage = jest.fn();
|
|
3769
3713
|
method.postProgressMessage = jest.fn();
|
|
3770
|
-
(method as any).
|
|
3771
|
-
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
3714
|
+
(method as any).protocolV2CommonUpdateProcess = jest.fn().mockResolvedValue(3);
|
|
3772
3715
|
(method as any).device = stubDevice({
|
|
3773
3716
|
getCommands: () => ({
|
|
3774
3717
|
typedCall: jest.fn().mockResolvedValue({
|
|
@@ -3814,14 +3757,11 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3814
3757
|
|
|
3815
3758
|
method.postTipMessage = jest.fn();
|
|
3816
3759
|
method.postProgressMessage = jest.fn();
|
|
3817
|
-
(method as any).
|
|
3760
|
+
(method as any).protocolV2CommonUpdateProcess = jest
|
|
3818
3761
|
.fn()
|
|
3819
3762
|
.mockImplementation(params =>
|
|
3820
|
-
Promise.resolve(Number(params.processedSize ?? 0) + Number(params.
|
|
3763
|
+
Promise.resolve(Number(params.processedSize ?? 0) + Number(params.payload.byteLength))
|
|
3821
3764
|
);
|
|
3822
|
-
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
3823
|
-
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
3824
|
-
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
3825
3765
|
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
3826
3766
|
(method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
|
|
3827
3767
|
(method as any).waitForProtocolV2FirmwareUpdateComplete = jest
|
|
@@ -4088,58 +4028,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4088
4028
|
getFirmwareLatestReleaseSpy.mockRestore();
|
|
4089
4029
|
});
|
|
4090
4030
|
|
|
4091
|
-
test('rejects prepared RESC bundle paths before opening an artifact source', async () => {
|
|
4092
|
-
const method = new FirmwareUpdateV4({
|
|
4093
|
-
id: 1,
|
|
4094
|
-
payload: {
|
|
4095
|
-
method: 'firmwareUpdateV4',
|
|
4096
|
-
},
|
|
4097
|
-
});
|
|
4098
|
-
const openPreparedSource = jest.fn();
|
|
4099
|
-
(method as any).params = {
|
|
4100
|
-
targetsToUpdate: ['resource'],
|
|
4101
|
-
resourceBundleArtifacts: [
|
|
4102
|
-
{
|
|
4103
|
-
name: 'images',
|
|
4104
|
-
artifact: {
|
|
4105
|
-
artifactRef: `fw:${'a'.repeat(64)}`,
|
|
4106
|
-
size: 1,
|
|
4107
|
-
sha256: 'a'.repeat(64),
|
|
4108
|
-
},
|
|
4109
|
-
},
|
|
4110
|
-
],
|
|
4111
|
-
};
|
|
4112
|
-
(method as any).openProtocolV2PreparedSource = openPreparedSource;
|
|
4113
|
-
const getFirmwareLatestReleaseSpy = jest
|
|
4114
|
-
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
4115
|
-
.mockReturnValue({
|
|
4116
|
-
required: false,
|
|
4117
|
-
version: [1, 0, 0],
|
|
4118
|
-
url: '',
|
|
4119
|
-
resourceBundles: [
|
|
4120
|
-
{
|
|
4121
|
-
name: 'images',
|
|
4122
|
-
url: 'https://example.com/images.okpkg',
|
|
4123
|
-
devicePath: 'vol0:/resource/../images.okpkg',
|
|
4124
|
-
},
|
|
4125
|
-
],
|
|
4126
|
-
fingerprint: '',
|
|
4127
|
-
changelog: {
|
|
4128
|
-
'zh-CN': '',
|
|
4129
|
-
'en-US': '',
|
|
4130
|
-
},
|
|
4131
|
-
});
|
|
4132
|
-
|
|
4133
|
-
await expect(
|
|
4134
|
-
(method as any).prepareProtocolV2ResourceSources('universal', {
|
|
4135
|
-
deviceType: 'pro2',
|
|
4136
|
-
})
|
|
4137
|
-
).rejects.toThrow('resourceBundles[].devicePath');
|
|
4138
|
-
expect(openPreparedSource).not.toHaveBeenCalled();
|
|
4139
|
-
|
|
4140
|
-
getFirmwareLatestReleaseSpy.mockRestore();
|
|
4141
|
-
});
|
|
4142
|
-
|
|
4143
4031
|
test('downloads and validates remote RESC bundles before entering bootloader', async () => {
|
|
4144
4032
|
const method = new FirmwareUpdateV4({
|
|
4145
4033
|
id: 1,
|
|
@@ -4192,9 +4080,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4192
4080
|
order.push('bootloader');
|
|
4193
4081
|
return Promise.resolve(true);
|
|
4194
4082
|
});
|
|
4195
|
-
|
|
4196
|
-
(method as any).executeProtocolV2TransferPhase = jest.fn().mockResolvedValue(undefined);
|
|
4197
|
-
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue(undefined);
|
|
4083
|
+
(method as any).executeProtocolV2Update = jest.fn().mockResolvedValue(undefined);
|
|
4198
4084
|
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
4199
4085
|
(method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({
|
|
4200
4086
|
bootloaderVersion: '1.0.0',
|
|
@@ -4206,7 +4092,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4206
4092
|
await method.run();
|
|
4207
4093
|
|
|
4208
4094
|
expect(order).toEqual(['download', 'bootloader']);
|
|
4209
|
-
expect(
|
|
4095
|
+
expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
|
|
4210
4096
|
bootloaderBinary: null,
|
|
4211
4097
|
fwBinaryMap: [],
|
|
4212
4098
|
resourceBundles: [
|
|
@@ -4219,7 +4105,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4219
4105
|
],
|
|
4220
4106
|
});
|
|
4221
4107
|
|
|
4222
|
-
executeProtocolV2UpdateSpy.mockRestore();
|
|
4223
4108
|
getSysResourceBinarySpy.mockRestore();
|
|
4224
4109
|
getFirmwareLatestReleaseSpy.mockRestore();
|
|
4225
4110
|
});
|
|
@@ -4508,10 +4393,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4508
4393
|
|
|
4509
4394
|
method.postTipMessage = jest.fn();
|
|
4510
4395
|
method.postProgressMessage = jest.fn();
|
|
4511
|
-
(method as any).
|
|
4512
|
-
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
4513
|
-
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
4514
|
-
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
4396
|
+
(method as any).protocolV2CommonUpdateProcess = jest.fn().mockResolvedValue(3);
|
|
4515
4397
|
(method as any).protocolV2StartFirmwareUpdate = jest.fn();
|
|
4516
4398
|
(method as any).waitForProtocolV2FirmwareUpdateComplete = jest.fn();
|
|
4517
4399
|
|
|
@@ -4527,7 +4409,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4527
4409
|
fwBinaryMap: [],
|
|
4528
4410
|
});
|
|
4529
4411
|
|
|
4530
|
-
expect((method as any).
|
|
4412
|
+
expect((method as any).protocolV2CommonUpdateProcess).toHaveBeenCalledTimes(1);
|
|
4531
4413
|
expect((method as any).protocolV2StartFirmwareUpdate).not.toHaveBeenCalled();
|
|
4532
4414
|
expect((method as any).waitForProtocolV2FirmwareUpdateComplete).not.toHaveBeenCalled();
|
|
4533
4415
|
});
|
|
@@ -4558,19 +4440,12 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4558
4440
|
});
|
|
4559
4441
|
method.postProgressMessage = jest.fn();
|
|
4560
4442
|
|
|
4561
|
-
|
|
4562
|
-
|
|
4443
|
+
await (method as any).protocolV2CommonUpdateProcess({
|
|
4444
|
+
payload: new Uint8Array(4097).buffer,
|
|
4445
|
+
filePath: 'vol1:firmware.bin',
|
|
4446
|
+
processedSize: 0,
|
|
4447
|
+
totalSize: 4097,
|
|
4563
4448
|
});
|
|
4564
|
-
try {
|
|
4565
|
-
await (method as any).protocolV2SourceUpdateProcess({
|
|
4566
|
-
source,
|
|
4567
|
-
filePath: 'vol1:firmware.bin',
|
|
4568
|
-
processedSize: 0,
|
|
4569
|
-
totalSize: 4097,
|
|
4570
|
-
});
|
|
4571
|
-
} finally {
|
|
4572
|
-
await source?.close();
|
|
4573
|
-
}
|
|
4574
4449
|
|
|
4575
4450
|
const writePayloads = typedCall.mock.calls.map(call => call[2]);
|
|
4576
4451
|
expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 4000]);
|
|
@@ -4621,16 +4496,12 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4621
4496
|
method.postProgressMessage = jest.fn();
|
|
4622
4497
|
|
|
4623
4498
|
try {
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
});
|
|
4627
|
-
await (method as any).protocolV2SourceUpdateProcess({
|
|
4628
|
-
source,
|
|
4499
|
+
await (method as any).protocolV2CommonUpdateProcess({
|
|
4500
|
+
payload: new Uint8Array(8001).buffer,
|
|
4629
4501
|
filePath: 'vol0:/firmware.bin',
|
|
4630
4502
|
processedSize: 0,
|
|
4631
4503
|
totalSize: 8001,
|
|
4632
4504
|
});
|
|
4633
|
-
await source?.close();
|
|
4634
4505
|
} finally {
|
|
4635
4506
|
setTimeoutSpy.mockRestore();
|
|
4636
4507
|
}
|
|
@@ -4662,21 +4533,16 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4662
4533
|
});
|
|
4663
4534
|
method.postProgressMessage = jest.fn();
|
|
4664
4535
|
method.postTipMessage = jest.fn();
|
|
4665
|
-
(method as any).recoverProtocolV2FileTransfer = jest.fn().mockResolvedValue(undefined);
|
|
4666
4536
|
|
|
4667
|
-
const source = await openFirmwareByteSource({
|
|
4668
|
-
binary: new Uint8Array(4097).buffer,
|
|
4669
|
-
});
|
|
4670
4537
|
await expect(
|
|
4671
|
-
(method as any).
|
|
4672
|
-
|
|
4538
|
+
(method as any).protocolV2WriteWholeFile({
|
|
4539
|
+
payload: new Uint8Array(4097).buffer,
|
|
4673
4540
|
filePath: 'vol0:/firmware.bin',
|
|
4674
4541
|
processedSize: 0,
|
|
4675
4542
|
totalSize: 4097,
|
|
4676
4543
|
})
|
|
4677
4544
|
).rejects.toMatchObject({ errorCode: HardwareErrorCode.EmmcFileWriteFirmwareError });
|
|
4678
|
-
|
|
4679
|
-
expect(typedCall).toHaveBeenCalledTimes(6);
|
|
4545
|
+
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
4680
4546
|
});
|
|
4681
4547
|
|
|
4682
4548
|
test('caps native BLE firmware upload chunks below the WebUSB limit', async () => {
|
|
@@ -4709,19 +4575,12 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4709
4575
|
});
|
|
4710
4576
|
method.postProgressMessage = jest.fn();
|
|
4711
4577
|
|
|
4712
|
-
|
|
4713
|
-
|
|
4578
|
+
await (method as any).protocolV2CommonUpdateProcess({
|
|
4579
|
+
payload: new Uint8Array(1801).buffer,
|
|
4580
|
+
filePath: 'vol1:ble-firmware.bin',
|
|
4581
|
+
processedSize: 0,
|
|
4582
|
+
totalSize: 1801,
|
|
4714
4583
|
});
|
|
4715
|
-
try {
|
|
4716
|
-
await (method as any).protocolV2SourceUpdateProcess({
|
|
4717
|
-
source,
|
|
4718
|
-
filePath: 'vol1:ble-firmware.bin',
|
|
4719
|
-
processedSize: 0,
|
|
4720
|
-
totalSize: 1801,
|
|
4721
|
-
});
|
|
4722
|
-
} finally {
|
|
4723
|
-
await source?.close();
|
|
4724
|
-
}
|
|
4725
4584
|
|
|
4726
4585
|
const writePayloads = typedCall.mock.calls.map(call => call[2]);
|
|
4727
4586
|
expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 1800]);
|
|
@@ -4838,9 +4697,9 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
4838
4697
|
const typedCall = jest
|
|
4839
4698
|
.fn()
|
|
4840
4699
|
.mockRejectedValue(
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4700
|
+
new DOMException(
|
|
4701
|
+
"Failed to execute 'open' on 'USBDevice': The device was disconnected",
|
|
4702
|
+
'NotFoundError'
|
|
4844
4703
|
)
|
|
4845
4704
|
);
|
|
4846
4705
|
|
|
@@ -5005,21 +4864,6 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
5005
4864
|
});
|
|
5006
4865
|
|
|
5007
4866
|
describe('Protocol V2 firmware reconnect identity', () => {
|
|
5008
|
-
test('keeps the host plan identity as the V4 reconnect anchor', () => {
|
|
5009
|
-
const method = new FirmwareUpdateV4({
|
|
5010
|
-
id: 1,
|
|
5011
|
-
payload: {
|
|
5012
|
-
method: 'firmwareUpdateV4',
|
|
5013
|
-
platform: 'native',
|
|
5014
|
-
expectedDeviceId: 'persisted-device-id',
|
|
5015
|
-
},
|
|
5016
|
-
});
|
|
5017
|
-
|
|
5018
|
-
method.init();
|
|
5019
|
-
|
|
5020
|
-
expect((method as any).params.expectedDeviceId).toBe('persisted-device-id');
|
|
5021
|
-
});
|
|
5022
|
-
|
|
5023
4867
|
test('rejects a different physical serial before firmware transfer resumes', () => {
|
|
5024
4868
|
expect(() => assertProtocolV2ReconnectIdentity('expected-serial', 'other-serial')).toThrow(
|
|
5025
4869
|
'identity mismatch'
|
|
@@ -5029,10 +4873,16 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
5029
4873
|
).not.toThrow();
|
|
5030
4874
|
});
|
|
5031
4875
|
|
|
5032
|
-
test('
|
|
5033
|
-
expect(() => assertProtocolV2ReconnectIdentity('expected-serial', undefined)).
|
|
5034
|
-
|
|
5035
|
-
|
|
4876
|
+
test('fails closed when the physical serial is unavailable', () => {
|
|
4877
|
+
expect(() => assertProtocolV2ReconnectIdentity('expected-serial', undefined)).toThrow(
|
|
4878
|
+
expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
|
|
4879
|
+
);
|
|
4880
|
+
expect(() => assertProtocolV2ReconnectIdentity(undefined, 'actual-serial')).toThrow(
|
|
4881
|
+
expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
|
|
4882
|
+
);
|
|
4883
|
+
expect(() => assertProtocolV2ReconnectIdentity(undefined, undefined)).toThrow(
|
|
4884
|
+
expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
|
|
4885
|
+
);
|
|
5036
4886
|
});
|
|
5037
4887
|
|
|
5038
4888
|
test('captures physical identity from active DeviceInfo instead of wallet deviceId', async () => {
|
|
@@ -5064,6 +4914,54 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
5064
4914
|
{ timeoutMs: 5000 }
|
|
5065
4915
|
);
|
|
5066
4916
|
});
|
|
4917
|
+
|
|
4918
|
+
test('rejects firmware update startup when DeviceInfo has no physical serial', async () => {
|
|
4919
|
+
const method = new FirmwareUpdateV4({
|
|
4920
|
+
id: 1,
|
|
4921
|
+
payload: { method: 'firmwareUpdateV4' },
|
|
4922
|
+
});
|
|
4923
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
4924
|
+
type: 'DeviceInfo',
|
|
4925
|
+
message: { protocol_version: 1, hw: {} },
|
|
4926
|
+
});
|
|
4927
|
+
(method as any).device = stubDevice({
|
|
4928
|
+
getCommands: () => ({ typedCall }),
|
|
4929
|
+
});
|
|
4930
|
+
|
|
4931
|
+
await expect((method as any).captureProtocolV2PhysicalIdentity()).rejects.toMatchObject({
|
|
4932
|
+
errorCode: HardwareErrorCode.DeviceNotFound,
|
|
4933
|
+
});
|
|
4934
|
+
});
|
|
4935
|
+
|
|
4936
|
+
test('uses the BLE read limit when reading an existing firmware bundle header', async () => {
|
|
4937
|
+
const method = new FirmwareUpdateV4({
|
|
4938
|
+
id: 1,
|
|
4939
|
+
payload: {
|
|
4940
|
+
method: 'firmwareUpdateV4',
|
|
4941
|
+
platform: 'native',
|
|
4942
|
+
chunkSize: 1800,
|
|
4943
|
+
},
|
|
4944
|
+
});
|
|
4945
|
+
method.init();
|
|
4946
|
+
const readChunkLengths: number[] = [];
|
|
4947
|
+
const typedCall = jest.fn((requestType: string, _responseType: string, request: any) => {
|
|
4948
|
+
if (requestType === 'FilesystemPathInfoQuery') {
|
|
4949
|
+
return Promise.resolve({ message: { exist: true, directory: false, size: 0x52a0 } });
|
|
4950
|
+
}
|
|
4951
|
+
if (requestType === 'FilesystemFileRead') {
|
|
4952
|
+
readChunkLengths.push(request.chunk_len);
|
|
4953
|
+
return Promise.resolve({ message: { data: new Uint8Array(request.chunk_len) } });
|
|
4954
|
+
}
|
|
4955
|
+
throw new Error(`Unexpected request: ${requestType}`);
|
|
4956
|
+
});
|
|
4957
|
+
(method as any).device = stubDevice({
|
|
4958
|
+
getCommands: () => ({ typedCall }),
|
|
4959
|
+
});
|
|
4960
|
+
|
|
4961
|
+
await expect((method as any).readProtocolV2DeviceFileHeader('bundle.okpp')).resolves.toBeNull();
|
|
4962
|
+
expect(readChunkLengths.length).toBeGreaterThan(1);
|
|
4963
|
+
expect(Math.max(...readChunkLengths)).toBe(900);
|
|
4964
|
+
});
|
|
5067
4965
|
});
|
|
5068
4966
|
|
|
5069
4967
|
describe('Protocol V2 explicit USB device selection', () => {
|
|
@@ -5245,6 +5143,70 @@ describe('Protocol V2 protected method execution', () => {
|
|
|
5245
5143
|
]);
|
|
5246
5144
|
});
|
|
5247
5145
|
|
|
5146
|
+
test('restores the expected hidden-wallet session before retrying after unlock', async () => {
|
|
5147
|
+
const calls: string[] = [];
|
|
5148
|
+
const method = {
|
|
5149
|
+
name: 'evmSignMessage',
|
|
5150
|
+
payload: { passphraseState: 'hidden-state' },
|
|
5151
|
+
useDevicePassphraseState: true,
|
|
5152
|
+
unlockPolicy: 'retry-on-locked',
|
|
5153
|
+
run: jest
|
|
5154
|
+
.fn()
|
|
5155
|
+
.mockImplementationOnce(() => {
|
|
5156
|
+
calls.push('run-1');
|
|
5157
|
+
return Promise.reject(deviceLockedError());
|
|
5158
|
+
})
|
|
5159
|
+
.mockImplementationOnce(() => {
|
|
5160
|
+
calls.push('run-2');
|
|
5161
|
+
return Promise.resolve({ message: 'ok' });
|
|
5162
|
+
}),
|
|
5163
|
+
};
|
|
5164
|
+
const typedCall = jest.fn((requestType: string, _responseType: string, request: any) => {
|
|
5165
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
5166
|
+
calls.push('negotiate-session');
|
|
5167
|
+
return Promise.resolve({ message: { version: 2 } });
|
|
5168
|
+
}
|
|
5169
|
+
if (requestType === 'DeviceSessionGet') {
|
|
5170
|
+
calls.push('resume-hidden-session');
|
|
5171
|
+
expect(request).toEqual({ session_id: 'hidden-session' });
|
|
5172
|
+
return Promise.resolve({
|
|
5173
|
+
message: {
|
|
5174
|
+
session_id: 'hidden-session',
|
|
5175
|
+
btc_test_address: 'hidden-state',
|
|
5176
|
+
},
|
|
5177
|
+
});
|
|
5178
|
+
}
|
|
5179
|
+
throw new Error(`Unexpected request: ${requestType}`);
|
|
5180
|
+
});
|
|
5181
|
+
const device = {
|
|
5182
|
+
features: { unlocked: true, passphraseProtection: true },
|
|
5183
|
+
passphraseState: 'hidden-state',
|
|
5184
|
+
commands: { typedCall },
|
|
5185
|
+
isProtocolV2: () => true,
|
|
5186
|
+
unlockDevice: jest.fn(() => {
|
|
5187
|
+
calls.push('unlock');
|
|
5188
|
+
return Promise.resolve();
|
|
5189
|
+
}),
|
|
5190
|
+
getCurrentPassphraseProtection: () => true,
|
|
5191
|
+
getInternalState: () => 'hidden-session',
|
|
5192
|
+
clearInternalState: jest.fn(),
|
|
5193
|
+
getCurrentDeviceId: () => 'wallet-device-id',
|
|
5194
|
+
updateInternalState: jest.fn(() => calls.push('validate-hidden-session')),
|
|
5195
|
+
};
|
|
5196
|
+
|
|
5197
|
+
await expect(runMethodWithUnlockRetry(method as any, device as any)).resolves.toEqual({
|
|
5198
|
+
message: 'ok',
|
|
5199
|
+
});
|
|
5200
|
+
expect(calls).toEqual([
|
|
5201
|
+
'run-1',
|
|
5202
|
+
'unlock',
|
|
5203
|
+
'negotiate-session',
|
|
5204
|
+
'resume-hidden-session',
|
|
5205
|
+
'validate-hidden-session',
|
|
5206
|
+
'run-2',
|
|
5207
|
+
]);
|
|
5208
|
+
});
|
|
5209
|
+
|
|
5248
5210
|
test('unlocks before showing the method interaction when cached status is locked', async () => {
|
|
5249
5211
|
const calls: string[] = [];
|
|
5250
5212
|
const method = {
|