@onekeyfe/hd-core 1.2.0-alpha.30 → 1.2.0-alpha.32
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__/check-all-firmware-release-protocol-v2.test.ts +251 -0
- package/__tests__/connectSettings.test.ts +5 -47
- package/__tests__/device-state-mapper.test.ts +10 -1
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
- package/__tests__/networkUtils.test.ts +20 -0
- package/__tests__/open-wallet-session.test.ts +19 -5
- package/__tests__/protocol-v2-firmware-targets.test.ts +18 -0
- package/__tests__/protocol-v2.test.ts +58 -240
- package/__tests__/public-device-state-api.test.ts +10 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts +24 -1
- 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 +6 -22
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/PromptWebDeviceAccess.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/stellar/StellarSignTransaction.d.ts +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/DeviceStateMapper.d.ts.map +1 -1
- package/dist/index.d.ts +43 -179
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1048 -2606
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- 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 +25 -7
- 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 +2 -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 +3 -10
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +9 -3
- package/dist/types/api/openWalletSession.d.ts.map +1 -1
- package/dist/types/device.d.ts +6 -0
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/settings.d.ts +1 -13
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/networkUtils.d.ts +1 -0
- package/dist/utils/networkUtils.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +368 -50
- package/src/api/FirmwareUpdateV2.ts +120 -298
- package/src/api/FirmwareUpdateV3.ts +56 -258
- package/src/api/FirmwareUpdateV4.ts +328 -765
- package/src/api/OpenWalletSession.ts +12 -8
- package/src/api/PromptWebDeviceAccess.ts +2 -6
- 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/data/messages/messages-protocol-v2.json +4 -0
- package/src/data-manager/DataManager.ts +33 -33
- package/src/data-manager/connectSettings.ts +0 -11
- package/src/device/DeviceStateMapper.ts +6 -0
- package/src/index.ts +0 -5
- package/src/inject.ts +2 -22
- package/src/lowLevelInject.ts +1 -5
- package/src/protocols/protocol-v2/walletSession.ts +2 -1
- package/src/topLevelInject.ts +1 -5
- package/src/types/api/checkAllFirmwareRelease.ts +46 -7
- package/src/types/api/deviceUpdateBootloader.ts +0 -6
- package/src/types/api/export.ts +5 -18
- package/src/types/api/firmwareUpdate.ts +0 -76
- package/src/types/api/index.ts +6 -15
- package/src/types/api/openWalletSession.ts +12 -3
- package/src/types/device.ts +6 -0
- package/src/types/settings.ts +3 -13
- package/src/utils/networkUtils.ts +5 -3
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -174
- 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-preparation-error.test.ts +0 -27
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
- package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
- 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/FirmwarePreparationError.d.ts +0 -3
- package/dist/api/firmware/FirmwarePreparationError.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 -25
- 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 -28
- 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/FirmwarePreparationError.ts +0 -12
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
- package/src/api/firmware/FirmwareUpdatePlan.ts +0 -745
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -392
- package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
- package/src/types/api/firmwareUpdatePlan.ts +0 -38
- package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { EFirmwareType } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import CheckAllFirmwareRelease, {
|
|
4
|
+
buildProtocolV2FirmwareRelease,
|
|
5
|
+
} from '../src/api/CheckAllFirmwareRelease';
|
|
6
|
+
import { DataManager } from '../src/data-manager';
|
|
7
|
+
import { createCoreApi } from '../src/inject';
|
|
8
|
+
|
|
9
|
+
import type { CoreApi } from '../src/types/api';
|
|
10
|
+
import type { DeviceStateVersions, IFirmwareReleaseInfo } from '../src/types';
|
|
11
|
+
|
|
12
|
+
jest.mock('../src/data/config', () => ({
|
|
13
|
+
DEFAULT_DOMAIN: 'https://example.com/',
|
|
14
|
+
getSDKVersion: () => '0.0.0-test',
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const currentVersions: DeviceStateVersions = {
|
|
18
|
+
firmware: '1.0.0',
|
|
19
|
+
bootloader: '1.0.0',
|
|
20
|
+
board: '1.0.0',
|
|
21
|
+
ble: '1.0.0',
|
|
22
|
+
se01: '1.0.0',
|
|
23
|
+
se02: '2.0.0',
|
|
24
|
+
se03: null,
|
|
25
|
+
se04: '1.0.0',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const release: IFirmwareReleaseInfo = {
|
|
29
|
+
required: true,
|
|
30
|
+
version: [2, 0, 0],
|
|
31
|
+
url: 'https://example.com/application-p1.bin',
|
|
32
|
+
fingerprint: 'release-fingerprint',
|
|
33
|
+
changelog: {
|
|
34
|
+
'zh-CN': '更新',
|
|
35
|
+
'en-US': 'Update',
|
|
36
|
+
},
|
|
37
|
+
installOrder: ['bootloader', 'applicationP1', 'se02', 'se03', 'romloader'],
|
|
38
|
+
components: {
|
|
39
|
+
bootloader: {
|
|
40
|
+
target: 'BOOTLOADER',
|
|
41
|
+
url: 'https://example.com/bootloader.bin',
|
|
42
|
+
version: [1, 1, 0],
|
|
43
|
+
},
|
|
44
|
+
applicationP1: {
|
|
45
|
+
target: 'APPLICATION_P1',
|
|
46
|
+
url: 'https://example.com/application-p1.bin',
|
|
47
|
+
version: [2, 0, 0],
|
|
48
|
+
},
|
|
49
|
+
se02: {
|
|
50
|
+
target: 'SE02',
|
|
51
|
+
url: 'https://example.com/se02.bin',
|
|
52
|
+
version: [2, 0, 0],
|
|
53
|
+
},
|
|
54
|
+
se03: {
|
|
55
|
+
target: 'SE03',
|
|
56
|
+
url: 'https://example.com/se03.bin',
|
|
57
|
+
version: [1, 0, 0],
|
|
58
|
+
},
|
|
59
|
+
romloader: {
|
|
60
|
+
target: 'ROMLOADER',
|
|
61
|
+
url: 'https://example.com/romloader.bin',
|
|
62
|
+
version: [2, 0, 0],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
resourceBundles: [
|
|
66
|
+
{
|
|
67
|
+
name: 'images',
|
|
68
|
+
url: 'https://example.com/images.okpkg',
|
|
69
|
+
devicePath: 'vol0:/bundles/images/images.okpkg',
|
|
70
|
+
version: [1, 0, 0],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
76
|
+
test('builds ordered firmwareUpdateV4 targets from recommended component versions', () => {
|
|
77
|
+
const result = buildProtocolV2FirmwareRelease({
|
|
78
|
+
currentVersions,
|
|
79
|
+
firmwareType: EFirmwareType.Universal,
|
|
80
|
+
release,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(result).toMatchObject({
|
|
84
|
+
status: 'required',
|
|
85
|
+
hasUpgrade: true,
|
|
86
|
+
required: true,
|
|
87
|
+
targetsToUpdate: ['boot', 'app_v1', 'resource'],
|
|
88
|
+
components: [
|
|
89
|
+
{ configKey: 'bootloader', status: 'outdated', updateTarget: 'boot' },
|
|
90
|
+
{ configKey: 'applicationP1', status: 'outdated', updateTarget: 'app_v1' },
|
|
91
|
+
{ configKey: 'se02', status: 'valid', updateTarget: 'se02' },
|
|
92
|
+
{ configKey: 'se03', status: 'unknown', updateTarget: 'se03' },
|
|
93
|
+
{ configKey: 'romloader', status: 'unsupported', updateTarget: null },
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('returns an unavailable plan when no Pro2 release is configured', () => {
|
|
99
|
+
expect(
|
|
100
|
+
buildProtocolV2FirmwareRelease({
|
|
101
|
+
currentVersions,
|
|
102
|
+
firmwareType: EFirmwareType.Universal,
|
|
103
|
+
release: undefined,
|
|
104
|
+
})
|
|
105
|
+
).toMatchObject({
|
|
106
|
+
status: 'unavailable',
|
|
107
|
+
hasUpgrade: false,
|
|
108
|
+
required: false,
|
|
109
|
+
components: [],
|
|
110
|
+
targetsToUpdate: [],
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('detects same-version package hotfixes by P1 and P2 payload hash', () => {
|
|
115
|
+
const packageSet: IFirmwareReleaseInfo = {
|
|
116
|
+
...release,
|
|
117
|
+
resourceBundles: undefined,
|
|
118
|
+
installOrder: ['applicationP1', 'applicationP2'],
|
|
119
|
+
components: {
|
|
120
|
+
applicationP1: {
|
|
121
|
+
target: 'APPLICATION_P1',
|
|
122
|
+
version: [1, 0, 0],
|
|
123
|
+
url: 'https://example.com/application-p1.okpkg',
|
|
124
|
+
fingerprint: 'file-sha256-p1',
|
|
125
|
+
},
|
|
126
|
+
applicationP2: {
|
|
127
|
+
target: 'APPLICATION_P2',
|
|
128
|
+
version: [1, 0, 0],
|
|
129
|
+
url: 'https://example.com/application-p2.okpkg',
|
|
130
|
+
fingerprint: 'file-sha256-p2',
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const result = buildProtocolV2FirmwareRelease({
|
|
136
|
+
currentVersions: {
|
|
137
|
+
...currentVersions,
|
|
138
|
+
applicationP1: '1.0.0',
|
|
139
|
+
applicationP2: '1.0.0',
|
|
140
|
+
},
|
|
141
|
+
currentVerification: {
|
|
142
|
+
applicationP1Hash: 'aa'.repeat(32),
|
|
143
|
+
applicationP2Hash: 'bb'.repeat(32),
|
|
144
|
+
},
|
|
145
|
+
remotePayloadHashes: {
|
|
146
|
+
applicationP1: 'aa'.repeat(64),
|
|
147
|
+
applicationP2: 'cc'.repeat(64),
|
|
148
|
+
},
|
|
149
|
+
firmwareType: EFirmwareType.Universal,
|
|
150
|
+
release: packageSet,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
expect(result).toMatchObject({
|
|
154
|
+
status: 'required',
|
|
155
|
+
targetsToUpdate: ['app_v2'],
|
|
156
|
+
components: [
|
|
157
|
+
{ configKey: 'applicationP1', status: 'valid' },
|
|
158
|
+
{ configKey: 'applicationP2', status: 'outdated' },
|
|
159
|
+
],
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('updates both application packages when normal mode only reports P1', () => {
|
|
164
|
+
const packageSet: IFirmwareReleaseInfo = {
|
|
165
|
+
...release,
|
|
166
|
+
resourceBundles: undefined,
|
|
167
|
+
installOrder: ['applicationP1', 'applicationP2'],
|
|
168
|
+
components: {
|
|
169
|
+
applicationP1: {
|
|
170
|
+
target: 'APPLICATION_P1',
|
|
171
|
+
version: [1, 0, 0],
|
|
172
|
+
url: 'https://example.com/application-p1.okpkg',
|
|
173
|
+
fingerprint: 'file-sha256-p1',
|
|
174
|
+
},
|
|
175
|
+
applicationP2: {
|
|
176
|
+
target: 'APPLICATION_P2',
|
|
177
|
+
version: [1, 0, 0],
|
|
178
|
+
url: 'https://example.com/application-p2.okpkg',
|
|
179
|
+
fingerprint: 'file-sha256-p2',
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const result = buildProtocolV2FirmwareRelease({
|
|
185
|
+
currentVersions,
|
|
186
|
+
currentVerification: { applicationP1Hash: 'aa'.repeat(32) },
|
|
187
|
+
remotePayloadHashes: { applicationP1: 'cc'.repeat(64) },
|
|
188
|
+
firmwareType: EFirmwareType.Universal,
|
|
189
|
+
release: packageSet,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(result.targetsToUpdate).toEqual(['app_v1', 'app_v2']);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('uses the existing method on Protocol V2 and keeps its public API name', async () => {
|
|
196
|
+
const method = new CheckAllFirmwareRelease({
|
|
197
|
+
id: 1,
|
|
198
|
+
payload: {
|
|
199
|
+
method: 'checkAllFirmwareRelease',
|
|
200
|
+
firmwareType: EFirmwareType.Universal,
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
method.init();
|
|
204
|
+
const getDeviceState = jest.fn().mockResolvedValue({
|
|
205
|
+
identity: {
|
|
206
|
+
deviceType: 'pro2',
|
|
207
|
+
firmwareType: EFirmwareType.Universal,
|
|
208
|
+
},
|
|
209
|
+
status: { mode: 'normal' },
|
|
210
|
+
versions: currentVersions,
|
|
211
|
+
});
|
|
212
|
+
method.device = {
|
|
213
|
+
isProtocolV2: () => true,
|
|
214
|
+
features: {
|
|
215
|
+
deviceType: 'pro2',
|
|
216
|
+
firmwareVersion: '1.0.0',
|
|
217
|
+
},
|
|
218
|
+
getDeviceState,
|
|
219
|
+
} as unknown as CheckAllFirmwareRelease['device'];
|
|
220
|
+
jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
|
|
221
|
+
|
|
222
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
223
|
+
protocol: 'V2',
|
|
224
|
+
deviceType: 'pro2',
|
|
225
|
+
status: 'required',
|
|
226
|
+
targetsToUpdate: ['boot', 'app_v1', 'resource'],
|
|
227
|
+
firmware: {
|
|
228
|
+
status: 'required',
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
expect(method.getSupportedProtocols()).toEqual(['V1', 'V2']);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test('continues forwarding the existing public method', async () => {
|
|
235
|
+
const call = jest.fn().mockResolvedValue({ success: true, payload: {} });
|
|
236
|
+
const api = createCoreApi(call as CoreApi['call']) as CoreApi;
|
|
237
|
+
|
|
238
|
+
await api.checkAllFirmwareRelease('pro2-connect-id', {
|
|
239
|
+
firmwareType: EFirmwareType.Universal,
|
|
240
|
+
retryCount: 0,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
expect(call).toHaveBeenCalledWith({
|
|
244
|
+
connectId: 'pro2-connect-id',
|
|
245
|
+
firmwareType: EFirmwareType.Universal,
|
|
246
|
+
retryCount: 0,
|
|
247
|
+
method: 'checkAllFirmwareRelease',
|
|
248
|
+
});
|
|
249
|
+
expect(api).not.toHaveProperty('checkAllFirmwareReleaseV4');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
@@ -1,61 +1,19 @@
|
|
|
1
|
-
import DataManager from '../src/data-manager/DataManager';
|
|
2
1
|
import { parseConnectSettings } from '../src/data-manager/connectSettings';
|
|
3
2
|
|
|
4
|
-
import type { RemoteConfigResponse } from '../src/types';
|
|
5
|
-
|
|
6
|
-
jest.mock('axios', () => ({
|
|
7
|
-
get: jest.fn(),
|
|
8
|
-
}));
|
|
9
|
-
|
|
10
3
|
jest.mock('../src/data/config', () => ({
|
|
11
4
|
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
12
5
|
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
13
6
|
}));
|
|
14
7
|
|
|
15
|
-
const mockAxiosGet: jest.Mock = jest.requireMock('axios').get;
|
|
16
|
-
|
|
17
8
|
describe('parseConnectSettings', () => {
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
});
|
|
9
|
+
it('keeps custom configFetcher in parsed settings', () => {
|
|
10
|
+
const configFetcher = jest.fn();
|
|
50
11
|
|
|
51
|
-
it('fails closed without a preloaded config in external-only mode', async () => {
|
|
52
12
|
const settings = parseConnectSettings({
|
|
53
|
-
|
|
13
|
+
fetchConfig: true,
|
|
14
|
+
configFetcher,
|
|
54
15
|
});
|
|
55
16
|
|
|
56
|
-
|
|
57
|
-
'External firmware config snapshot is required'
|
|
58
|
-
);
|
|
59
|
-
expect(mockAxiosGet).not.toHaveBeenCalled();
|
|
17
|
+
expect(settings.configFetcher).toBe(configFetcher);
|
|
60
18
|
});
|
|
61
19
|
});
|
|
@@ -111,7 +111,8 @@ describe('DeviceStateMapper', () => {
|
|
|
111
111
|
protocol_version: 2,
|
|
112
112
|
hw: { serial_no: 'SERIAL-1' },
|
|
113
113
|
fw: {
|
|
114
|
-
application: { version: '5.0.0' },
|
|
114
|
+
application: { version: '5.0.0', build_id: 'p1-build', hash: [0x01, 0x02] },
|
|
115
|
+
application_data: { version: '5.0.1', build_id: 'p2-build', hash: [0x03, 0x04] },
|
|
115
116
|
bootloader: { version: '2.0.0' },
|
|
116
117
|
},
|
|
117
118
|
coprocessor: {
|
|
@@ -129,9 +130,17 @@ describe('DeviceStateMapper', () => {
|
|
|
129
130
|
});
|
|
130
131
|
expect(patch.versions).toMatchObject({
|
|
131
132
|
firmware: '5.0.0',
|
|
133
|
+
applicationP1: '5.0.0',
|
|
134
|
+
applicationP2: '5.0.1',
|
|
132
135
|
bootloader: '2.0.0',
|
|
133
136
|
ble: '1.2.3',
|
|
134
137
|
});
|
|
138
|
+
expect(patch.verification).toMatchObject({
|
|
139
|
+
applicationP1BuildId: 'p1-build',
|
|
140
|
+
applicationP1Hash: '0102',
|
|
141
|
+
applicationP2BuildId: 'p2-build',
|
|
142
|
+
applicationP2Hash: '0304',
|
|
143
|
+
});
|
|
135
144
|
expect(patch).toMatchObject({ protocolVersion: 2 });
|
|
136
145
|
expect(patch.status?.unlocked).toBeUndefined();
|
|
137
146
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { EDeviceType, EFirmwareType,
|
|
1
|
+
import { EDeviceType, EFirmwareType, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import FirmwareUpdateV2 from '../../src/api/FirmwareUpdateV2';
|
|
4
4
|
import { getBinary } from '../../src/api/firmware/getBinary';
|
|
5
|
-
import {
|
|
5
|
+
import { uploadFirmware } from '../../src/api/firmware/uploadFirmware';
|
|
6
6
|
import * as utils from '../../src/utils';
|
|
7
7
|
|
|
8
8
|
jest.mock('../../src/data/config', () => ({
|
|
@@ -18,8 +18,7 @@ jest.mock('../../src/api/firmware/getBinary', () => ({
|
|
|
18
18
|
|
|
19
19
|
jest.mock('../../src/api/firmware/uploadFirmware', () => ({
|
|
20
20
|
updateResources: jest.fn(),
|
|
21
|
-
|
|
22
|
-
uploadFirmwareFromSource: jest.fn(),
|
|
21
|
+
uploadFirmware: jest.fn(),
|
|
23
22
|
}));
|
|
24
23
|
|
|
25
24
|
jest.mock('../../src/device/DevicePool', () => ({
|
|
@@ -30,9 +29,7 @@ jest.mock('../../src/device/DevicePool', () => ({
|
|
|
30
29
|
}));
|
|
31
30
|
|
|
32
31
|
const mockGetBinary = getBinary as jest.MockedFunction<typeof getBinary>;
|
|
33
|
-
const
|
|
34
|
-
typeof uploadFirmwareFromSource
|
|
35
|
-
>;
|
|
32
|
+
const mockUploadFirmware = uploadFirmware as jest.MockedFunction<typeof uploadFirmware>;
|
|
36
33
|
|
|
37
34
|
type DeviceType = 'CLASSIC1S' | 'PURE';
|
|
38
35
|
|
|
@@ -178,26 +175,7 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
178
175
|
expect(mockGetBinary).toHaveBeenCalledTimes(1);
|
|
179
176
|
expectNoFirmwareMutationCalls(typedCall);
|
|
180
177
|
expect(acquire).not.toHaveBeenCalled();
|
|
181
|
-
expect(
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('preserves structured preparation errors before any firmware mutation', async () => {
|
|
185
|
-
const preparationError = ERRORS.TypedError(
|
|
186
|
-
HardwareErrorCode.RuntimeError,
|
|
187
|
-
'Firmware must be prepared by the external firmware host',
|
|
188
|
-
{
|
|
189
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
190
|
-
artifactName: 'firmware',
|
|
191
|
-
}
|
|
192
|
-
);
|
|
193
|
-
mockGetBinary.mockRejectedValue(preparationError);
|
|
194
|
-
const { method, typedCall, acquire } = createMethod();
|
|
195
|
-
|
|
196
|
-
await expect(method.run()).rejects.toBe(preparationError);
|
|
197
|
-
|
|
198
|
-
expectNoFirmwareMutationCalls(typedCall);
|
|
199
|
-
expect(acquire).not.toHaveBeenCalled();
|
|
200
|
-
expect(mockUploadFirmwareFromSource).not.toHaveBeenCalled();
|
|
178
|
+
expect(mockUploadFirmware).not.toHaveBeenCalled();
|
|
201
179
|
});
|
|
202
180
|
|
|
203
181
|
it.each([
|
|
@@ -238,11 +216,9 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
238
216
|
binary: firmwareBinary,
|
|
239
217
|
} as Awaited<ReturnType<typeof getBinary>>);
|
|
240
218
|
});
|
|
241
|
-
|
|
219
|
+
mockUploadFirmware.mockImplementation(() => {
|
|
242
220
|
callOrder.push('upload');
|
|
243
|
-
return Promise.resolve(
|
|
244
|
-
expectedResponse as Awaited<ReturnType<typeof uploadFirmwareFromSource>>
|
|
245
|
-
);
|
|
221
|
+
return Promise.resolve(expectedResponse as Awaited<ReturnType<typeof uploadFirmware>>);
|
|
246
222
|
});
|
|
247
223
|
const { method, typedCall } = createMethod({
|
|
248
224
|
updateType,
|
|
@@ -272,15 +248,15 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
272
248
|
},
|
|
273
249
|
})
|
|
274
250
|
);
|
|
275
|
-
expect(
|
|
251
|
+
expect(mockUploadFirmware).toHaveBeenCalledWith(
|
|
276
252
|
updateType,
|
|
277
253
|
expect.any(Function),
|
|
278
254
|
expect.any(Function),
|
|
279
255
|
method.device,
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
256
|
+
{
|
|
257
|
+
payload: firmwareBinary,
|
|
258
|
+
rebootOnSuccess: true,
|
|
259
|
+
},
|
|
284
260
|
isUpdateBootloader
|
|
285
261
|
);
|
|
286
262
|
}
|
|
@@ -295,11 +271,11 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
295
271
|
binary: firmwareBinary,
|
|
296
272
|
} as Awaited<ReturnType<typeof getBinary>>);
|
|
297
273
|
});
|
|
298
|
-
|
|
274
|
+
mockUploadFirmware.mockImplementation(() => {
|
|
299
275
|
callOrder.push('upload');
|
|
300
276
|
return Promise.resolve({
|
|
301
277
|
success: true,
|
|
302
|
-
} as Awaited<ReturnType<typeof
|
|
278
|
+
} as Awaited<ReturnType<typeof uploadFirmware>>);
|
|
303
279
|
});
|
|
304
280
|
const { method, typedCall, acquire } = createMethod({
|
|
305
281
|
bootloaderMode: true,
|
|
@@ -324,9 +300,9 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
324
300
|
resolveDownload = resolve;
|
|
325
301
|
})
|
|
326
302
|
);
|
|
327
|
-
|
|
303
|
+
mockUploadFirmware.mockResolvedValue({
|
|
328
304
|
success: true,
|
|
329
|
-
} as Awaited<ReturnType<typeof
|
|
305
|
+
} as Awaited<ReturnType<typeof uploadFirmware>>);
|
|
330
306
|
const { method, typedCall } = createMethod();
|
|
331
307
|
|
|
332
308
|
const runPromise = method.run();
|
|
@@ -357,7 +333,7 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
357
333
|
|
|
358
334
|
expectNoFirmwareMutationCalls(typedCall);
|
|
359
335
|
expect(acquire).not.toHaveBeenCalled();
|
|
360
|
-
expect(
|
|
336
|
+
expect(mockUploadFirmware).not.toHaveBeenCalled();
|
|
361
337
|
});
|
|
362
338
|
|
|
363
339
|
it.each([
|
|
@@ -366,23 +342,23 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
366
342
|
])(
|
|
367
343
|
'keeps the non-empty %s binary overload and never downloads it again',
|
|
368
344
|
async (_kind, binary) => {
|
|
369
|
-
|
|
345
|
+
mockUploadFirmware.mockResolvedValue({
|
|
370
346
|
success: true,
|
|
371
|
-
} as Awaited<ReturnType<typeof
|
|
347
|
+
} as Awaited<ReturnType<typeof uploadFirmware>>);
|
|
372
348
|
const { method } = createMethod({ binary });
|
|
373
349
|
|
|
374
350
|
await method.run();
|
|
375
351
|
|
|
376
352
|
expect(mockGetBinary).not.toHaveBeenCalled();
|
|
377
|
-
expect(
|
|
353
|
+
expect(mockUploadFirmware).toHaveBeenCalledWith(
|
|
378
354
|
'firmware',
|
|
379
355
|
expect.any(Function),
|
|
380
356
|
expect.any(Function),
|
|
381
357
|
method.device,
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
358
|
+
{
|
|
359
|
+
payload: binary,
|
|
360
|
+
rebootOnSuccess: true,
|
|
361
|
+
},
|
|
386
362
|
undefined
|
|
387
363
|
);
|
|
388
364
|
}
|
|
@@ -393,21 +369,17 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
393
369
|
['DataView', new DataView(new Uint8Array([9, 3, 4, 9]).buffer, 1, 2), [3, 4]],
|
|
394
370
|
['custom Buffer', createCustomBuffer(4), [1, 2, 3, 4]],
|
|
395
371
|
])('normalizes a non-empty %s before rebooting', async (_kind, binary, expectedBytes) => {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
uploadedBytes = Array.from(new Uint8Array(await source.readAt(0, source.size)));
|
|
400
|
-
return {
|
|
401
|
-
success: true,
|
|
402
|
-
} as Awaited<ReturnType<typeof uploadFirmwareFromSource>>;
|
|
403
|
-
}
|
|
404
|
-
);
|
|
372
|
+
mockUploadFirmware.mockResolvedValue({
|
|
373
|
+
success: true,
|
|
374
|
+
} as Awaited<ReturnType<typeof uploadFirmware>>);
|
|
405
375
|
const { method } = createMethod({ binary });
|
|
406
376
|
|
|
407
377
|
await method.run();
|
|
408
378
|
|
|
409
379
|
expect(mockGetBinary).not.toHaveBeenCalled();
|
|
410
|
-
|
|
380
|
+
const normalizedBinary = mockUploadFirmware.mock.calls[0][4].payload;
|
|
381
|
+
expect(normalizedBinary).toBeInstanceOf(ArrayBuffer);
|
|
382
|
+
expect(Array.from(new Uint8Array(normalizedBinary))).toEqual(expectedBytes);
|
|
411
383
|
});
|
|
412
384
|
|
|
413
385
|
it.each([
|
|
@@ -425,6 +397,6 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
425
397
|
expect(mockGetBinary).not.toHaveBeenCalled();
|
|
426
398
|
expectNoFirmwareMutationCalls(typedCall);
|
|
427
399
|
expect(acquire).not.toHaveBeenCalled();
|
|
428
|
-
expect(
|
|
400
|
+
expect(mockUploadFirmware).not.toHaveBeenCalled();
|
|
429
401
|
});
|
|
430
402
|
});
|
|
@@ -27,6 +27,26 @@ describe('networkUtils httpRequest retry bounds', () => {
|
|
|
27
27
|
expect(requestSpy.mock.calls[0][0]).not.toHaveProperty('onDownloadProgress');
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
it('accepts partial content and forwards byte-range headers', async () => {
|
|
31
|
+
const packageHeader = new ArrayBuffer(640);
|
|
32
|
+
const requestSpy = jest.spyOn(axios, 'request').mockResolvedValue({
|
|
33
|
+
status: 206,
|
|
34
|
+
data: packageHeader,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await expect(
|
|
38
|
+
httpRequest('https://example.com/application-p1.okpkg', 'binary', {
|
|
39
|
+
headers: { Range: 'bytes=0-639' },
|
|
40
|
+
})
|
|
41
|
+
).resolves.toBe(packageHeader);
|
|
42
|
+
|
|
43
|
+
expect(requestSpy).toHaveBeenCalledWith(
|
|
44
|
+
expect.objectContaining({
|
|
45
|
+
headers: { Range: 'bytes=0-639' },
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
30
50
|
it('passes the per-attempt timeout and stops after the configured retry count', async () => {
|
|
31
51
|
const timeoutError = Object.assign(new Error('timeout'), {
|
|
32
52
|
code: 'ECONNABORTED',
|
|
@@ -101,12 +101,15 @@ describe('openWalletSession', () => {
|
|
|
101
101
|
expect(promptPassphrase).toHaveBeenCalledWith(
|
|
102
102
|
{
|
|
103
103
|
existsAttachPinUser: false,
|
|
104
|
+
deviceOnly: true,
|
|
104
105
|
source: 'wallet-session-coordinator',
|
|
105
106
|
reason: 'open-wallet',
|
|
106
107
|
},
|
|
107
108
|
{ cancelDeviceOnReject: false }
|
|
108
109
|
);
|
|
109
|
-
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
110
|
+
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
111
|
+
on_device: true,
|
|
112
|
+
});
|
|
110
113
|
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
|
|
111
114
|
});
|
|
112
115
|
|
|
@@ -221,7 +224,9 @@ describe('openWalletSession', () => {
|
|
|
221
224
|
passphraseState: 'hidden-state',
|
|
222
225
|
});
|
|
223
226
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
224
|
-
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
227
|
+
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
228
|
+
on_device: true,
|
|
229
|
+
});
|
|
225
230
|
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
|
|
226
231
|
});
|
|
227
232
|
|
|
@@ -298,7 +303,9 @@ describe('openWalletSession', () => {
|
|
|
298
303
|
expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
299
304
|
eventless_wallet_session: true,
|
|
300
305
|
});
|
|
301
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
306
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
307
|
+
on_device: true,
|
|
308
|
+
});
|
|
302
309
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
303
310
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
304
311
|
expect(deviceWalletSessionStore.get('device-1', 'new-hidden-state')).toBe('new-hidden-session');
|
|
@@ -590,7 +597,9 @@ describe('openWalletSession', () => {
|
|
|
590
597
|
passphraseState: 'hidden-state',
|
|
591
598
|
resumed: false,
|
|
592
599
|
});
|
|
593
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
600
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
601
|
+
on_device: true,
|
|
602
|
+
});
|
|
594
603
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
595
604
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
596
605
|
expect(device.passphraseState).toBeUndefined();
|
|
@@ -616,7 +625,9 @@ describe('openWalletSession', () => {
|
|
|
616
625
|
walletType: 'hidden',
|
|
617
626
|
passphraseState: 'device-state',
|
|
618
627
|
});
|
|
619
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
628
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
629
|
+
on_device: true,
|
|
630
|
+
});
|
|
620
631
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
621
632
|
});
|
|
622
633
|
|
|
@@ -649,6 +660,7 @@ describe('openWalletSession', () => {
|
|
|
649
660
|
expect(promptPassphrase).toHaveBeenCalledWith(
|
|
650
661
|
{
|
|
651
662
|
existsAttachPinUser: false,
|
|
663
|
+
deviceOnly: true,
|
|
652
664
|
source: 'wallet-session-coordinator',
|
|
653
665
|
reason: 'open-wallet',
|
|
654
666
|
},
|
|
@@ -656,6 +668,7 @@ describe('openWalletSession', () => {
|
|
|
656
668
|
);
|
|
657
669
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
658
670
|
passphrase: 'host hidden wallet',
|
|
671
|
+
on_device: false,
|
|
659
672
|
});
|
|
660
673
|
});
|
|
661
674
|
|
|
@@ -682,6 +695,7 @@ describe('openWalletSession', () => {
|
|
|
682
695
|
expect(promptPassphrase).toHaveBeenCalledWith(
|
|
683
696
|
{
|
|
684
697
|
existsAttachPinUser: true,
|
|
698
|
+
deviceOnly: true,
|
|
685
699
|
source: 'wallet-session-coordinator',
|
|
686
700
|
reason: 'open-wallet',
|
|
687
701
|
},
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { ProtocolV2FirmwareTargetType } from '../src/protocols/protocol-v2/firmware';
|
|
2
|
+
import { isProtocolV2FirmwareFingerprintValid } from '../src/api/FirmwareUpdateV4';
|
|
3
|
+
|
|
4
|
+
jest.mock('../src/data/config', () => ({
|
|
5
|
+
DEFAULT_DOMAIN: 'https://example.com/',
|
|
6
|
+
getSDKVersion: () => '0.0.0-test',
|
|
7
|
+
}));
|
|
2
8
|
|
|
3
9
|
describe('Protocol V2 firmware target contract', () => {
|
|
4
10
|
test('matches the current firmware-pro2 target ids', () => {
|
|
@@ -16,4 +22,16 @@ describe('Protocol V2 firmware target contract', () => {
|
|
|
16
22
|
FW_MGMT_TARGET_SE04: 10,
|
|
17
23
|
});
|
|
18
24
|
});
|
|
25
|
+
|
|
26
|
+
test('validates the full package SHA-256 fingerprint from config', () => {
|
|
27
|
+
const binary = Uint8Array.from([1, 2, 3]);
|
|
28
|
+
|
|
29
|
+
expect(
|
|
30
|
+
isProtocolV2FirmwareFingerprintValid(
|
|
31
|
+
binary,
|
|
32
|
+
'039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81'
|
|
33
|
+
)
|
|
34
|
+
).toBe(true);
|
|
35
|
+
expect(isProtocolV2FirmwareFingerprintValid(binary, '00'.repeat(32))).toBe(false);
|
|
36
|
+
});
|
|
19
37
|
});
|