@onekeyfe/hd-core 1.2.0-alpha.40 → 1.2.0-alpha.41
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__/AllNetworkGetAddressBase.tracing.test.ts +0 -78
- package/__tests__/DeviceEventRegistration.test.ts +0 -6
- package/__tests__/connectSettings.test.ts +47 -5
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +175 -0
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
- package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
- package/__tests__/firmware-update/firmware-preparation-error.test.ts +27 -0
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +593 -0
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +213 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +59 -31
- package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +58 -0
- package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
- package/__tests__/get-device-state.test.ts +12 -132
- package/__tests__/homescreen.test.ts +1 -41
- package/__tests__/logBlockEvent.test.ts +1 -1
- package/__tests__/open-wallet-session.test.ts +18 -259
- package/__tests__/protocol-v2-unlock-policy.test.ts +35 -86
- package/__tests__/protocol-v2.test.ts +632 -333
- package/__tests__/public-pro2-api-boundary.test.ts +0 -1
- package/dist/api/BaseMethod.d.ts +1 -3
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +11 -0
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts +7 -3
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +22 -5
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +25 -0
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
- package/dist/api/firmware/FirmwarePreparationError.d.ts +3 -0
- package/dist/api/firmware/FirmwarePreparationError.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +8 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +25 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +30 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
- package/dist/api/firmware/getBinary.d.ts +7 -0
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/progressThrottle.d.ts +3 -0
- package/dist/api/firmware/progressThrottle.d.ts.map +1 -0
- package/dist/api/firmware/updateBootloader.d.ts +2 -0
- package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts +9 -1
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts +0 -2
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +0 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
- package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
- package/dist/core/deviceEventRegistration.d.ts +0 -2
- package/dist/core/deviceEventRegistration.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +1 -0
- 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 +4 -22
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/events/device.d.ts +0 -4
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +2 -27
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +181 -88
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3162 -1614
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/lockedError.d.ts +2 -0
- package/dist/protocols/protocol-v2/lockedError.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/uiInteraction.d.ts +3 -3
- package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts +3 -1
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts +10 -0
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts +1 -4
- 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 +4 -0
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
- package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +4 -1
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +69 -0
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
- package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdatePlan.d.ts +28 -0
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
- package/dist/types/api/index.d.ts +10 -3
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +0 -3
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/settings.d.ts +13 -0
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +0 -2
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/homescreen.d.ts +0 -4
- package/dist/utils/homescreen.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/dist/utils/pro2Wallpaper.d.ts +0 -10
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +10 -8
- package/src/api/CheckAllFirmwareRelease.ts +51 -6
- package/src/api/FirmwareUpdateV2.ts +298 -120
- package/src/api/FirmwareUpdateV3.ts +263 -57
- package/src/api/FirmwareUpdateV4.ts +748 -281
- package/src/api/GetPassphraseState.ts +1 -7
- package/src/api/OpenWalletSession.ts +17 -54
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +11 -32
- package/src/api/device/DeviceUnlock.ts +1 -1
- package/src/api/device/DeviceUpdateBootloader.ts +122 -6
- package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
- package/src/api/firmware/FirmwareHostBinding.ts +100 -0
- package/src/api/firmware/FirmwarePreparationError.ts +12 -0
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +57 -0
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
- package/src/api/firmware/FirmwareUpdatePlan.ts +745 -0
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +392 -0
- package/src/api/firmware/progressThrottle.ts +44 -0
- package/src/api/firmware/updateBootloader.ts +19 -4
- package/src/api/firmware/uploadFirmware.ts +140 -22
- package/src/api/helpers/protocolV2FileWrite.ts +4 -11
- package/src/api/index.ts +0 -1
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +1 -3
- package/src/core/deviceEventRegistration.ts +0 -4
- package/src/core/index.ts +75 -118
- package/src/data/messages/messages-protocol-v2.json +0 -10
- package/src/data-manager/DataManager.ts +33 -33
- package/src/data-manager/connectSettings.ts +11 -0
- package/src/device/Device.ts +21 -193
- package/src/events/device.ts +0 -4
- package/src/events/logBlockEvent.ts +0 -1
- package/src/events/ui-request.ts +2 -32
- package/src/index.ts +5 -0
- package/src/inject.ts +22 -3
- package/src/lowLevelInject.ts +5 -1
- package/src/protocols/protocol-v2/features.ts +2 -9
- package/src/protocols/protocol-v2/lockedError.ts +10 -0
- package/src/protocols/protocol-v2/uiInteraction.ts +5 -31
- package/src/protocols/protocol-v2/unlockPolicy.ts +105 -1
- package/src/protocols/protocol-v2/unlockRetry.ts +109 -0
- package/src/protocols/protocol-v2/walletSession.ts +18 -93
- package/src/topLevelInject.ts +5 -1
- package/src/types/api/checkAllFirmwareRelease.ts +4 -0
- package/src/types/api/deviceUpdateBootloader.ts +6 -0
- package/src/types/api/export.ts +18 -0
- package/src/types/api/firmwareUpdate.ts +76 -0
- package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
- package/src/types/api/firmwareUpdatePlan.ts +38 -0
- package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
- package/src/types/api/index.ts +14 -2
- package/src/types/api/protocolV2.ts +0 -13
- package/src/types/settings.ts +13 -0
- package/src/utils/deviceFeaturesUtils.ts +0 -4
- package/src/utils/homescreen.ts +6 -32
- package/src/utils/index.ts +1 -6
- package/src/utils/pro2Wallpaper.ts +8 -35
- package/__tests__/deviceUploadNft.test.ts +0 -187
- package/__tests__/pro2Nft.test.ts +0 -108
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +0 -56
- package/__tests__/uiProgressThrottle.test.ts +0 -74
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +0 -30
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +0 -22
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +0 -1
- package/dist/utils/pro2Nft.d.ts +0 -29
- package/dist/utils/pro2Nft.d.ts.map +0 -1
- package/dist/utils/uiProgressThrottle.d.ts +0 -3
- package/dist/utils/uiProgressThrottle.d.ts.map +0 -1
- package/src/api/protocol-v2/DeviceUploadNft.ts +0 -166
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +0 -117
- package/src/utils/pro2Nft.ts +0 -91
- package/src/utils/uiProgressThrottle.ts +0 -63
|
@@ -18,83 +18,6 @@ class TestAllNetworkMethod extends AllNetworkGetAddressBase {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
describe('AllNetworkGetAddressBase tracing', () => {
|
|
21
|
-
test('resumes a Protocol V2 hidden wallet before running a nested chain method', async () => {
|
|
22
|
-
const calls: string[] = [];
|
|
23
|
-
const checkPassphraseStateSafety = jest.fn().mockImplementation(() => {
|
|
24
|
-
calls.push('resume-hidden-session');
|
|
25
|
-
return Promise.resolve(true);
|
|
26
|
-
});
|
|
27
|
-
const innerMethod = {
|
|
28
|
-
checkSafetyLevelOnTestNet: jest.fn().mockResolvedValue(false),
|
|
29
|
-
connectId: 'connect-id',
|
|
30
|
-
deviceId: 'device-id',
|
|
31
|
-
getVersionRange: jest.fn().mockReturnValue({}),
|
|
32
|
-
assertProtocolSupported: jest.fn(),
|
|
33
|
-
init: jest.fn(),
|
|
34
|
-
name: 'evmGetAddress',
|
|
35
|
-
responseID: 43,
|
|
36
|
-
unlockPolicy: 'unlock-before-run',
|
|
37
|
-
run: jest.fn().mockImplementation(() => {
|
|
38
|
-
calls.push('run-chain-method');
|
|
39
|
-
return Promise.resolve([{ address: '0xhidden' }]);
|
|
40
|
-
}),
|
|
41
|
-
setDevice: jest.fn(),
|
|
42
|
-
strictCheckDeviceSupport: false,
|
|
43
|
-
};
|
|
44
|
-
(findMethod as jest.Mock).mockReturnValue(innerMethod);
|
|
45
|
-
const method = new TestAllNetworkMethod({
|
|
46
|
-
id: 1,
|
|
47
|
-
payload: {
|
|
48
|
-
method: 'allNetworkGetAddress',
|
|
49
|
-
connectId: 'connect-id',
|
|
50
|
-
deviceId: 'device-id',
|
|
51
|
-
passphraseState: 'hidden-state',
|
|
52
|
-
bundle: [],
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
method.protocolV2UnlockContext = { preflightCompleted: true };
|
|
56
|
-
const typedCall = jest.fn();
|
|
57
|
-
method.device = {
|
|
58
|
-
checkPassphraseStateSafety,
|
|
59
|
-
commands: {
|
|
60
|
-
typedCall,
|
|
61
|
-
},
|
|
62
|
-
getCurrentFirmwareType: jest.fn(),
|
|
63
|
-
getProtocol: jest.fn().mockReturnValue('V2'),
|
|
64
|
-
getCurrentFirmwareVersionString: jest.fn().mockReturnValue('1.0.0'),
|
|
65
|
-
getCurrentMethodVersionRange: jest
|
|
66
|
-
.fn()
|
|
67
|
-
.mockImplementation((getRange: (type: string) => unknown) => getRange('pro2')),
|
|
68
|
-
instanceId: 'device-instance',
|
|
69
|
-
isProtocolV2: jest.fn().mockReturnValue(true),
|
|
70
|
-
isBootloader: jest.fn().mockReturnValue(false),
|
|
71
|
-
isRomloader: jest.fn().mockReturnValue(false),
|
|
72
|
-
off: jest.fn(),
|
|
73
|
-
on: jest.fn(),
|
|
74
|
-
state: { status: { unlocked: true } },
|
|
75
|
-
updateProtocolV2Status: jest.fn(),
|
|
76
|
-
} as any;
|
|
77
|
-
|
|
78
|
-
await method.callMethod(
|
|
79
|
-
'evmGetAddress',
|
|
80
|
-
{
|
|
81
|
-
bundle: [
|
|
82
|
-
{
|
|
83
|
-
_originRequestParams: {
|
|
84
|
-
network: 'evm',
|
|
85
|
-
path: "m/44'/60'/0'/0/0",
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
},
|
|
90
|
-
0
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
expect(checkPassphraseStateSafety).toHaveBeenCalledWith('hidden-state', false, undefined);
|
|
94
|
-
expect(calls).toEqual(['resume-hidden-session', 'run-chain-method']);
|
|
95
|
-
expect(typedCall).not.toHaveBeenCalled();
|
|
96
|
-
});
|
|
97
|
-
|
|
98
21
|
test('releases the nested request context when an unhandled error escapes', async () => {
|
|
99
22
|
const deviceInstanceId = 'device-instance';
|
|
100
23
|
const innerMethod = {
|
|
@@ -128,7 +51,6 @@ describe('AllNetworkGetAddressBase tracing', () => {
|
|
|
128
51
|
getCurrentMethodVersionRange: jest
|
|
129
52
|
.fn()
|
|
130
53
|
.mockImplementation((getRange: (type: string) => unknown) => getRange('classic')),
|
|
131
|
-
isProtocolV2: jest.fn().mockReturnValue(false),
|
|
132
54
|
off: jest.fn(),
|
|
133
55
|
on: jest.fn(),
|
|
134
56
|
} as any;
|
|
@@ -3,8 +3,6 @@ import { registerHardwareUiEventListeners } from '../src/core/deviceEventRegistr
|
|
|
3
3
|
|
|
4
4
|
const handlers = {
|
|
5
5
|
pin: jest.fn(),
|
|
6
|
-
pinOnDevice: jest.fn(),
|
|
7
|
-
pinOnDeviceComplete: jest.fn(),
|
|
8
6
|
button: jest.fn(),
|
|
9
7
|
passphrase: jest.fn(),
|
|
10
8
|
passphraseOnDevice: jest.fn(),
|
|
@@ -23,8 +21,6 @@ describe('hardware UI event registration', () => {
|
|
|
23
21
|
expect(registerHardwareUiEventListeners(device as any, handlers)).toBe(true);
|
|
24
22
|
expect(device.on.mock.calls.map(([type]) => type)).toEqual([
|
|
25
23
|
DEVICE.PIN,
|
|
26
|
-
DEVICE.PIN_ON_DEVICE,
|
|
27
|
-
DEVICE.PIN_ON_DEVICE_COMPLETE,
|
|
28
24
|
DEVICE.BUTTON,
|
|
29
25
|
DEVICE.PASSPHRASE,
|
|
30
26
|
DEVICE.PASSPHRASE_ON_DEVICE,
|
|
@@ -38,8 +34,6 @@ describe('hardware UI event registration', () => {
|
|
|
38
34
|
expect(registerHardwareUiEventListeners(device as any, handlers)).toBe(true);
|
|
39
35
|
expect(device.on.mock.calls.map(([type]) => type)).toEqual([
|
|
40
36
|
DEVICE.PIN,
|
|
41
|
-
DEVICE.PIN_ON_DEVICE,
|
|
42
|
-
DEVICE.PIN_ON_DEVICE_COMPLETE,
|
|
43
37
|
DEVICE.BUTTON,
|
|
44
38
|
DEVICE.PASSPHRASE,
|
|
45
39
|
DEVICE.PASSPHRASE_ON_DEVICE,
|
|
@@ -1,19 +1,61 @@
|
|
|
1
|
+
import DataManager from '../src/data-manager/DataManager';
|
|
1
2
|
import { parseConnectSettings } from '../src/data-manager/connectSettings';
|
|
2
3
|
|
|
4
|
+
import type { RemoteConfigResponse } from '../src/types';
|
|
5
|
+
|
|
6
|
+
jest.mock('axios', () => ({
|
|
7
|
+
get: jest.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
3
10
|
jest.mock('../src/data/config', () => ({
|
|
4
11
|
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
5
12
|
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
6
13
|
}));
|
|
7
14
|
|
|
15
|
+
const mockAxiosGet: jest.Mock = jest.requireMock('axios').get;
|
|
16
|
+
|
|
8
17
|
describe('parseConnectSettings', () => {
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
const preloadedConfig = {
|
|
19
|
+
bridge: {},
|
|
20
|
+
classic: { firmware: [], ble: [] },
|
|
21
|
+
classic1s: { firmware: [], ble: [] },
|
|
22
|
+
classicpure: { firmware: [], ble: [] },
|
|
23
|
+
mini: { firmware: [], ble: [] },
|
|
24
|
+
touch: { firmware: [], ble: [] },
|
|
25
|
+
pro: { firmware: [], ble: [] },
|
|
26
|
+
pro2: { firmware: [], ble: [] },
|
|
27
|
+
} as unknown as RemoteConfigResponse;
|
|
28
|
+
|
|
29
|
+
it('keeps a serializable preloaded config in parsed settings', () => {
|
|
30
|
+
const settings = parseConnectSettings({
|
|
31
|
+
firmwareManifestMode: 'sdk-managed',
|
|
32
|
+
preloadedConfig,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(settings.firmwareManifestMode).toBe('sdk-managed');
|
|
36
|
+
expect(settings.preloadedConfig).toBe(preloadedConfig);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('uses the preloaded config before the SDK network client', async () => {
|
|
40
|
+
const settings = parseConnectSettings({
|
|
41
|
+
firmwareManifestMode: 'sdk-managed',
|
|
42
|
+
preloadedConfig,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
await DataManager.load(settings);
|
|
46
|
+
|
|
47
|
+
expect(mockAxiosGet).not.toHaveBeenCalled();
|
|
48
|
+
expect(DataManager.deviceMap.classic).toEqual({ firmware: [], ble: [] });
|
|
49
|
+
});
|
|
11
50
|
|
|
51
|
+
it('fails closed without a preloaded config in external-only mode', async () => {
|
|
12
52
|
const settings = parseConnectSettings({
|
|
13
|
-
|
|
14
|
-
configFetcher,
|
|
53
|
+
firmwareManifestMode: 'external-only',
|
|
15
54
|
});
|
|
16
55
|
|
|
17
|
-
expect(settings
|
|
56
|
+
await expect(DataManager.load(settings)).rejects.toThrow(
|
|
57
|
+
'External firmware config snapshot is required'
|
|
58
|
+
);
|
|
59
|
+
expect(mockAxiosGet).not.toHaveBeenCalled();
|
|
18
60
|
});
|
|
19
61
|
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { EDeviceType, EFirmwareType, ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import CheckAllFirmwareRelease from '../../src/api/CheckAllFirmwareRelease';
|
|
4
|
+
import { buildFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePlan';
|
|
5
|
+
import {
|
|
6
|
+
getBleFirmwareReleaseInfo,
|
|
7
|
+
getBootloaderReleaseInfo,
|
|
8
|
+
getFirmwareReleaseInfo,
|
|
9
|
+
} from '../../src/api/firmware/releaseHelper';
|
|
10
|
+
|
|
11
|
+
jest.mock('../../src/data/config', () => ({
|
|
12
|
+
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
13
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock('../../src/api/firmware/FirmwareUpdatePlan', () => ({
|
|
17
|
+
buildFirmwareUpdatePlan: jest.fn(),
|
|
18
|
+
validateFirmwareUpdatePlanForceTargets: jest.fn(value => value ?? []),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
jest.mock('../../src/api/firmware/releaseHelper', () => ({
|
|
22
|
+
getBleFirmwareReleaseInfo: jest.fn(),
|
|
23
|
+
getBootloaderReleaseInfo: jest.fn(),
|
|
24
|
+
getFirmwareReleaseInfo: jest.fn(),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
const mockBuildFirmwareUpdatePlan = buildFirmwareUpdatePlan as jest.MockedFunction<
|
|
28
|
+
typeof buildFirmwareUpdatePlan
|
|
29
|
+
>;
|
|
30
|
+
const mockGetBleFirmwareReleaseInfo = getBleFirmwareReleaseInfo as jest.MockedFunction<
|
|
31
|
+
typeof getBleFirmwareReleaseInfo
|
|
32
|
+
>;
|
|
33
|
+
const mockGetBootloaderReleaseInfo = getBootloaderReleaseInfo as jest.MockedFunction<
|
|
34
|
+
typeof getBootloaderReleaseInfo
|
|
35
|
+
>;
|
|
36
|
+
const mockGetFirmwareReleaseInfo = getFirmwareReleaseInfo as jest.MockedFunction<
|
|
37
|
+
typeof getFirmwareReleaseInfo
|
|
38
|
+
>;
|
|
39
|
+
|
|
40
|
+
const noUpdate = {
|
|
41
|
+
status: 'valid' as const,
|
|
42
|
+
release: undefined,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const createMethod = (serialNo = 'device-id') => {
|
|
46
|
+
const method = new CheckAllFirmwareRelease({
|
|
47
|
+
id: 1,
|
|
48
|
+
payload: {
|
|
49
|
+
method: 'checkAllFirmwareRelease',
|
|
50
|
+
platform: 'native',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
method.init();
|
|
54
|
+
method.device = {
|
|
55
|
+
isProtocolV2: jest.fn(() => false),
|
|
56
|
+
features: {
|
|
57
|
+
deviceType: EDeviceType.Classic1s,
|
|
58
|
+
serialNo,
|
|
59
|
+
firmwareType: EFirmwareType.Universal,
|
|
60
|
+
firmwareVersion: '1.0.0',
|
|
61
|
+
bootloaderVersion: '1.0.0',
|
|
62
|
+
},
|
|
63
|
+
} as typeof method.device;
|
|
64
|
+
return method;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const createMethodWithForceTargets = () => {
|
|
68
|
+
const method = createMethod();
|
|
69
|
+
method.payload = {
|
|
70
|
+
...method.payload,
|
|
71
|
+
forceUpdateTargets: ['firmware', 'resource'],
|
|
72
|
+
};
|
|
73
|
+
return method;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
describe('CheckAllFirmwareRelease', () => {
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
jest.clearAllMocks();
|
|
79
|
+
mockGetFirmwareReleaseInfo.mockReturnValue(noUpdate);
|
|
80
|
+
mockGetBootloaderReleaseInfo.mockReturnValue(noUpdate);
|
|
81
|
+
mockGetBleFirmwareReleaseInfo.mockReturnValue(noUpdate);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('keeps release information available when an optional Plan cannot be built', async () => {
|
|
85
|
+
const firmware = {
|
|
86
|
+
status: 'outdated' as const,
|
|
87
|
+
release: {
|
|
88
|
+
url: 'https://firmware.onekey.so/classic/firmware.bin',
|
|
89
|
+
version: [3, 0, 0],
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
mockGetFirmwareReleaseInfo.mockReturnValue(firmware);
|
|
93
|
+
mockBuildFirmwareUpdatePlan.mockImplementation(() => {
|
|
94
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Plan cannot be built', {
|
|
95
|
+
firmwareUpdateCode: 'FirmwarePlanInvalid',
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await expect(createMethod('').run()).resolves.toEqual(
|
|
100
|
+
expect.objectContaining({
|
|
101
|
+
firmware,
|
|
102
|
+
bootloader: noUpdate,
|
|
103
|
+
ble: noUpdate,
|
|
104
|
+
firmwareUpdatePlan: undefined,
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('returns an optional Plan when the builder succeeds', async () => {
|
|
110
|
+
const plan = {
|
|
111
|
+
schemaVersion: 2 as const,
|
|
112
|
+
planDigest: 'a'.repeat(64),
|
|
113
|
+
executor: 'v3' as const,
|
|
114
|
+
deviceIdentity: 'device-id',
|
|
115
|
+
deviceModel: EDeviceType.Classic1s,
|
|
116
|
+
firmwareType: EFirmwareType.Universal,
|
|
117
|
+
platform: 'native' as const,
|
|
118
|
+
artifacts: [],
|
|
119
|
+
targetsToUpdate: [],
|
|
120
|
+
};
|
|
121
|
+
mockBuildFirmwareUpdatePlan.mockReturnValue(plan);
|
|
122
|
+
|
|
123
|
+
await expect(createMethod().run()).resolves.toEqual(
|
|
124
|
+
expect.objectContaining({
|
|
125
|
+
firmwareUpdatePlan: plan,
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('passes host-selected update targets to the Plan builder', async () => {
|
|
131
|
+
mockBuildFirmwareUpdatePlan.mockReturnValue({
|
|
132
|
+
schemaVersion: 2,
|
|
133
|
+
planDigest: 'a'.repeat(64),
|
|
134
|
+
executor: 'v2',
|
|
135
|
+
deviceIdentity: 'device-id',
|
|
136
|
+
deviceModel: EDeviceType.Classic1s,
|
|
137
|
+
firmwareType: EFirmwareType.Universal,
|
|
138
|
+
platform: 'native',
|
|
139
|
+
artifacts: [],
|
|
140
|
+
targetsToUpdate: [],
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await createMethodWithForceTargets().run();
|
|
144
|
+
|
|
145
|
+
expect(mockBuildFirmwareUpdatePlan).toHaveBeenCalledWith(
|
|
146
|
+
expect.objectContaining({
|
|
147
|
+
forceUpdateTargets: ['firmware', 'resource'],
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('does not hide a Plan failure for an explicitly forced target', async () => {
|
|
153
|
+
const planError = ERRORS.TypedError(
|
|
154
|
+
HardwareErrorCode.RuntimeError,
|
|
155
|
+
'Forced firmware target is unavailable',
|
|
156
|
+
{
|
|
157
|
+
firmwareUpdateCode: 'FirmwarePlanInvalid',
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
mockBuildFirmwareUpdatePlan.mockImplementation(() => {
|
|
161
|
+
throw planError;
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
await expect(createMethodWithForceTargets().run()).rejects.toBe(planError);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('does not hide unexpected Plan builder failures', async () => {
|
|
168
|
+
const unexpectedError = new Error('unexpected builder failure');
|
|
169
|
+
mockBuildFirmwareUpdatePlan.mockImplementation(() => {
|
|
170
|
+
throw unexpectedError;
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
await expect(createMethod().run()).rejects.toBe(unexpectedError);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_FIRMWARE_ARTIFACT_READ_BYTES,
|
|
3
|
+
openFirmwareByteSource,
|
|
4
|
+
writeFirmwareByteSource,
|
|
5
|
+
} from '../../src/api/firmware/FirmwareArtifactSource';
|
|
6
|
+
|
|
7
|
+
import type { FirmwareArtifactReader } from '../../src/types/api/firmwareUpdate';
|
|
8
|
+
|
|
9
|
+
const artifact = {
|
|
10
|
+
artifactRef: `fw:${'a'.repeat(64)}`,
|
|
11
|
+
size: 6,
|
|
12
|
+
sha256: 'b'.repeat(64),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const createReader = () => {
|
|
16
|
+
const open = jest.fn(() => Promise.resolve({ readerId: 'reader-1', size: artifact.size }));
|
|
17
|
+
const read = jest.fn(({ offset, length }: { readerId: string; offset: number; length: number }) =>
|
|
18
|
+
Promise.resolve({
|
|
19
|
+
data: Uint8Array.from([1, 2, 3, 4, 5, 6]).slice(offset, offset + length).buffer,
|
|
20
|
+
bytesRead: length,
|
|
21
|
+
eof: offset + length === artifact.size,
|
|
22
|
+
})
|
|
23
|
+
);
|
|
24
|
+
const close = jest.fn(() => Promise.resolve());
|
|
25
|
+
const reader: FirmwareArtifactReader = { open, read, close };
|
|
26
|
+
return { reader, open, read, close };
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
describe('FirmwareArtifactSource', () => {
|
|
30
|
+
it('reads an opaque artifact through bounded sequential reads', async () => {
|
|
31
|
+
const { reader, close } = createReader();
|
|
32
|
+
const source = await openFirmwareByteSource({ artifact, reader });
|
|
33
|
+
if (!source) {
|
|
34
|
+
throw new Error('Expected a firmware byte source');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const writes: Array<{ offset: number; data: number[] }> = [];
|
|
38
|
+
await writeFirmwareByteSource({
|
|
39
|
+
source,
|
|
40
|
+
chunkSize: 4,
|
|
41
|
+
write: ({ data, sourceOffset, length }) => {
|
|
42
|
+
writes.push({
|
|
43
|
+
offset: sourceOffset,
|
|
44
|
+
data: Array.from(new Uint8Array(data)),
|
|
45
|
+
});
|
|
46
|
+
return Promise.resolve(length);
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
await source.close();
|
|
50
|
+
|
|
51
|
+
expect(writes).toEqual([
|
|
52
|
+
{ offset: 0, data: [1, 2, 3, 4] },
|
|
53
|
+
{ offset: 4, data: [5, 6] },
|
|
54
|
+
]);
|
|
55
|
+
expect(close).toHaveBeenCalledWith({ readerId: 'reader-1' });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('rejects a reader whose opened size differs from the receipt', async () => {
|
|
59
|
+
const { reader, open, close } = createReader();
|
|
60
|
+
open.mockResolvedValue({
|
|
61
|
+
readerId: 'reader-1',
|
|
62
|
+
size: artifact.size + 1,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await expect(openFirmwareByteSource({ artifact, reader })).rejects.toMatchObject({
|
|
66
|
+
params: {
|
|
67
|
+
firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch',
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
expect(close).toHaveBeenCalledWith({ readerId: 'reader-1' });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('rejects paths and over-limit reads before invoking the host', async () => {
|
|
74
|
+
const { reader, read } = createReader();
|
|
75
|
+
await expect(
|
|
76
|
+
openFirmwareByteSource({
|
|
77
|
+
artifact: { ...artifact, artifactRef: '/tmp/firmware.bin' },
|
|
78
|
+
reader,
|
|
79
|
+
})
|
|
80
|
+
).rejects.toMatchObject({
|
|
81
|
+
params: {
|
|
82
|
+
firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch',
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const source = await openFirmwareByteSource({ artifact, reader });
|
|
87
|
+
if (!source) {
|
|
88
|
+
throw new Error('Expected a firmware byte source');
|
|
89
|
+
}
|
|
90
|
+
await expect(
|
|
91
|
+
writeFirmwareByteSource({
|
|
92
|
+
source,
|
|
93
|
+
chunkSize: MAX_FIRMWARE_ARTIFACT_READ_BYTES + 1,
|
|
94
|
+
write: ({ length }) => Promise.resolve(length),
|
|
95
|
+
})
|
|
96
|
+
).rejects.toMatchObject({
|
|
97
|
+
params: {
|
|
98
|
+
firmwareUpdateCode: 'FirmwareArtifactReadTooLarge',
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
expect(read).not.toHaveBeenCalled();
|
|
102
|
+
await source.close();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('closes stale readers when open returns invalid metadata', async () => {
|
|
106
|
+
const { reader, open, close } = createReader();
|
|
107
|
+
open.mockResolvedValue({
|
|
108
|
+
readerId: '',
|
|
109
|
+
size: artifact.size,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await expect(openFirmwareByteSource({ artifact, reader })).rejects.toMatchObject({
|
|
113
|
+
params: {
|
|
114
|
+
firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch',
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
expect(close).not.toHaveBeenCalled();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getFirmwareUpdateHostBindingGeneration,
|
|
3
|
+
registerFirmwareUpdateHostBinding,
|
|
4
|
+
resolveFirmwareUpdateHostBinding,
|
|
5
|
+
unregisterFirmwareUpdateHostBinding,
|
|
6
|
+
} from '../../src/api/firmware/FirmwareHostBinding';
|
|
7
|
+
|
|
8
|
+
const createBinding = () => ({
|
|
9
|
+
artifactReader: {
|
|
10
|
+
open: jest.fn(() => Promise.resolve({ readerId: 'reader-1', size: 4 })),
|
|
11
|
+
read: jest.fn(() =>
|
|
12
|
+
Promise.resolve({
|
|
13
|
+
data: new ArrayBuffer(4),
|
|
14
|
+
bytesRead: 4,
|
|
15
|
+
eof: true,
|
|
16
|
+
})
|
|
17
|
+
),
|
|
18
|
+
close: jest.fn(() => Promise.resolve()),
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('firmware host binding generation', () => {
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
unregisterFirmwareUpdateHostBinding();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('invalidates callbacks from a replaced generation', async () => {
|
|
28
|
+
const staleBinding = createBinding();
|
|
29
|
+
const staleGeneration = registerFirmwareUpdateHostBinding(staleBinding);
|
|
30
|
+
const stale = resolveFirmwareUpdateHostBinding(staleGeneration);
|
|
31
|
+
const activeGeneration = registerFirmwareUpdateHostBinding(createBinding());
|
|
32
|
+
|
|
33
|
+
expect(activeGeneration).toBeGreaterThan(staleGeneration);
|
|
34
|
+
expect(getFirmwareUpdateHostBindingGeneration()).toBe(activeGeneration);
|
|
35
|
+
await expect(
|
|
36
|
+
stale.artifactReader.read({
|
|
37
|
+
readerId: 'reader-1',
|
|
38
|
+
offset: 0,
|
|
39
|
+
length: 4,
|
|
40
|
+
})
|
|
41
|
+
).rejects.toMatchObject({
|
|
42
|
+
params: {
|
|
43
|
+
firmwareUpdateCode: 'FirmwareArtifactReaderInvalid',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
expect(unregisterFirmwareUpdateHostBinding(staleGeneration)).toBe(false);
|
|
47
|
+
expect(getFirmwareUpdateHostBindingGeneration()).toBe(activeGeneration);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('closes an artifact opened during a generation race', async () => {
|
|
51
|
+
const binding = createBinding();
|
|
52
|
+
binding.artifactReader.open.mockImplementationOnce(() => {
|
|
53
|
+
registerFirmwareUpdateHostBinding(createBinding());
|
|
54
|
+
return Promise.resolve({ readerId: 'stale-reader', size: 4 });
|
|
55
|
+
});
|
|
56
|
+
const generation = registerFirmwareUpdateHostBinding(binding);
|
|
57
|
+
const guarded = resolveFirmwareUpdateHostBinding(generation);
|
|
58
|
+
|
|
59
|
+
await expect(
|
|
60
|
+
guarded.artifactReader.open({ artifactRef: `fw:${'a'.repeat(64)}` })
|
|
61
|
+
).rejects.toMatchObject({
|
|
62
|
+
params: {
|
|
63
|
+
firmwareUpdateCode: 'FirmwareArtifactReaderInvalid',
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
expect(binding.artifactReader.close).toHaveBeenCalledWith({
|
|
67
|
+
readerId: 'stale-reader',
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import { normalizeFirmwarePreparationError } from '../../src/api/firmware/FirmwarePreparationError';
|
|
4
|
+
|
|
5
|
+
describe('normalizeFirmwarePreparationError', () => {
|
|
6
|
+
test('preserves structured firmware invariant errors', () => {
|
|
7
|
+
const error = ERRORS.TypedError(
|
|
8
|
+
HardwareErrorCode.RuntimeError,
|
|
9
|
+
'Firmware must be prepared by the external firmware host',
|
|
10
|
+
{
|
|
11
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
12
|
+
artifactName: 'firmware',
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
expect(normalizeFirmwarePreparationError(error)).toBe(error);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('wraps ordinary preparation failures as firmware download errors', () => {
|
|
20
|
+
const error = normalizeFirmwarePreparationError(new Error('request failed'));
|
|
21
|
+
|
|
22
|
+
expect(error).toMatchObject({
|
|
23
|
+
errorCode: HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
24
|
+
message: 'request failed',
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getFirmwareUpdateCapabilities } from '../../src/api/firmware/FirmwareUpdateCapabilities';
|
|
2
|
+
|
|
3
|
+
describe('getFirmwareUpdateCapabilities', () => {
|
|
4
|
+
it('advertises only the prepared artifact contract', () => {
|
|
5
|
+
expect(getFirmwareUpdateCapabilities()).toEqual({
|
|
6
|
+
planSchemaVersion: 2,
|
|
7
|
+
preparedPlanSchemaVersion: 2,
|
|
8
|
+
hostBindingProtocolVersion: 2,
|
|
9
|
+
manifestModes: ['external-only', 'sdk-managed'],
|
|
10
|
+
supportsArtifactReader: true,
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
});
|