@onekeyfe/hd-core 1.2.0-alpha.34 → 1.2.0-alpha.36
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 +74 -0
- package/__tests__/DeviceCommands.test.ts +84 -0
- package/__tests__/DeviceEventRegistration.test.ts +6 -0
- package/__tests__/device-settings.test.ts +3 -0
- package/__tests__/device-wallet-session-store.test.ts +80 -7
- package/__tests__/evmSignTransaction.test.ts +69 -0
- package/__tests__/get-device-state.test.ts +180 -40
- package/__tests__/open-wallet-session.test.ts +314 -55
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +56 -0
- package/__tests__/protocol-v2.test.ts +768 -139
- package/dist/api/FirmwareUpdateV4.d.ts +1 -0
- 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/evm/EVMSignTypedData.d.ts.map +1 -1
- package/dist/api/evm/protocol.d.ts +3 -0
- package/dist/api/evm/protocol.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +1 -1
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
- package/dist/core/deviceEventRegistration.d.ts +2 -0
- package/dist/core/deviceEventRegistration.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +25 -6
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DeviceWalletSessionStore.d.ts +0 -1
- package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
- package/dist/events/device.d.ts +4 -0
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +27 -2
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +63 -12
- package/dist/index.js +1164 -904
- package/dist/protocols/protocol-v2/features.d.ts +17 -6
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +2 -2
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +1 -0
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
- 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/unlockRetry.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +5 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +1 -1
- package/dist/types/api/openWalletSession.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +2 -2
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +2 -1
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +2 -0
- package/dist/utils/deviceFeaturesUtils.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/BaseMethod.ts +1 -1
- package/src/api/FirmwareUpdateV4.ts +39 -14
- package/src/api/GetPassphraseState.ts +17 -0
- package/src/api/OpenWalletSession.ts +31 -8
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +24 -2
- package/src/api/device/DeviceUnlock.ts +1 -1
- package/src/api/evm/EVMGetAddress.ts +2 -2
- package/src/api/evm/EVMGetPublicKey.ts +2 -2
- package/src/api/evm/EVMSignMessage.ts +2 -2
- package/src/api/evm/EVMSignTransaction.ts +2 -2
- package/src/api/evm/EVMSignTypedData.ts +35 -45
- package/src/api/evm/EVMVerifyMessage.ts +2 -2
- package/src/api/evm/protocol.ts +6 -0
- package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +2 -2
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +1 -1
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
- package/src/core/deviceEventRegistration.ts +4 -0
- package/src/core/index.ts +46 -4
- package/src/data/messages/messages-protocol-v2.json +408 -485
- package/src/device/Device.ts +266 -43
- package/src/device/DeviceCommands.ts +12 -2
- package/src/device/DeviceWalletSessionStore.ts +0 -14
- package/src/deviceProfile/buildDeviceFeatures.ts +1 -1
- package/src/events/device.ts +4 -0
- package/src/events/ui-request.ts +32 -2
- package/src/protocols/protocol-v2/features.ts +65 -16
- package/src/protocols/protocol-v2/index.ts +5 -0
- package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +8 -3
- package/src/protocols/protocol-v2/uiInteraction.ts +31 -5
- package/src/protocols/protocol-v2/unlockRetry.ts +51 -18
- package/src/protocols/protocol-v2/walletSession.ts +172 -64
- package/src/types/api/openWalletSession.ts +1 -1
- package/src/types/api/protocolV2.ts +2 -2
- package/src/types/device.ts +2 -0
- package/src/utils/deviceFeaturesUtils.ts +4 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
1
|
+
import { EDeviceType, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { DeviceType } from '@onekeyfe/hd-transport';
|
|
2
3
|
|
|
3
4
|
import { Device } from '../src/device/Device';
|
|
4
5
|
import { DEVICE } from '../src/events';
|
|
6
|
+
import { PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE } from '../src/protocols/protocol-v2';
|
|
5
7
|
|
|
6
8
|
jest.mock('../src/data/config', () => ({
|
|
7
9
|
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
@@ -28,7 +30,83 @@ const createV1Device = (typedCall: jest.Mock) => {
|
|
|
28
30
|
return device;
|
|
29
31
|
};
|
|
30
32
|
|
|
33
|
+
const protocolV2ApplicationInfo = {
|
|
34
|
+
version: 1,
|
|
35
|
+
build_fingerprint: 'application__5.0.0__abcdef0__PROD__RELEASE',
|
|
36
|
+
supported_messages: [PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const getProtocolV2LoaderInfo = (mode: 'bootloader' | 'romloader') => ({
|
|
40
|
+
version: 1,
|
|
41
|
+
build_fingerprint: `${mode}__1.0.0__abcdef0__PROD__RELEASE`,
|
|
42
|
+
supported_messages: [],
|
|
43
|
+
});
|
|
44
|
+
|
|
31
45
|
describe('getDeviceState', () => {
|
|
46
|
+
test('coalesces concurrent Protocol V2 runtime-context negotiation', async () => {
|
|
47
|
+
let resolveProtocolInfo:
|
|
48
|
+
| ((value: { message: typeof protocolV2ApplicationInfo }) => void)
|
|
49
|
+
| undefined;
|
|
50
|
+
const typedCall = jest.fn(
|
|
51
|
+
() =>
|
|
52
|
+
new Promise<{ message: typeof protocolV2ApplicationInfo }>(resolve => {
|
|
53
|
+
resolveProtocolInfo = resolve;
|
|
54
|
+
})
|
|
55
|
+
);
|
|
56
|
+
const device = createV2Device(typedCall);
|
|
57
|
+
|
|
58
|
+
const first = device.ensureProtocolV2RuntimeContext();
|
|
59
|
+
const second = device.ensureProtocolV2RuntimeContext();
|
|
60
|
+
resolveProtocolInfo?.({ message: protocolV2ApplicationInfo });
|
|
61
|
+
|
|
62
|
+
await expect(Promise.all([first, second])).resolves.toEqual([
|
|
63
|
+
protocolV2ApplicationInfo,
|
|
64
|
+
protocolV2ApplicationInfo,
|
|
65
|
+
]);
|
|
66
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
67
|
+
expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
68
|
+
eventless_wallet_session: true,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('renegotiates Protocol V2 runtime context after transport disconnect', async () => {
|
|
73
|
+
const typedCall = jest.fn().mockResolvedValue({ message: protocolV2ApplicationInfo });
|
|
74
|
+
const device = createV2Device(typedCall);
|
|
75
|
+
|
|
76
|
+
await device.ensureProtocolV2RuntimeContext();
|
|
77
|
+
await device.ensureProtocolV2RuntimeContext();
|
|
78
|
+
device.markTransportDisconnected();
|
|
79
|
+
await device.ensureProtocolV2RuntimeContext();
|
|
80
|
+
|
|
81
|
+
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('rejects every waiter when runtime-context negotiation is invalidated', async () => {
|
|
85
|
+
let resolveProtocolInfo:
|
|
86
|
+
| ((value: { message: typeof protocolV2ApplicationInfo }) => void)
|
|
87
|
+
| undefined;
|
|
88
|
+
const typedCall = jest.fn(
|
|
89
|
+
() =>
|
|
90
|
+
new Promise<{ message: typeof protocolV2ApplicationInfo }>(resolve => {
|
|
91
|
+
resolveProtocolInfo = resolve;
|
|
92
|
+
})
|
|
93
|
+
);
|
|
94
|
+
const device = createV2Device(typedCall);
|
|
95
|
+
|
|
96
|
+
const first = device.ensureProtocolV2RuntimeContext();
|
|
97
|
+
const second = device.ensureProtocolV2RuntimeContext();
|
|
98
|
+
const firstExpectation = expect(first).rejects.toMatchObject({
|
|
99
|
+
errorCode: HardwareErrorCode.DeviceInitializeFailed,
|
|
100
|
+
});
|
|
101
|
+
const secondExpectation = expect(second).rejects.toMatchObject({
|
|
102
|
+
errorCode: HardwareErrorCode.DeviceInitializeFailed,
|
|
103
|
+
});
|
|
104
|
+
device.markTransportDisconnected();
|
|
105
|
+
resolveProtocolInfo?.({ message: protocolV2ApplicationInfo });
|
|
106
|
+
|
|
107
|
+
await Promise.all([firstExpectation, secondExpectation]);
|
|
108
|
+
});
|
|
109
|
+
|
|
32
110
|
test('does not expose the internal wallet session', async () => {
|
|
33
111
|
const device = createV2Device(jest.fn());
|
|
34
112
|
device.updateState({ protocol: 'V2' }, 'initialize');
|
|
@@ -39,45 +117,55 @@ describe('getDeviceState', () => {
|
|
|
39
117
|
});
|
|
40
118
|
|
|
41
119
|
test('hydrates Protocol V2 with separate DeviceInfoGet and DeviceStatusGet', async () => {
|
|
42
|
-
const typedCall = jest.fn().mockImplementation((requestType: string) =>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
120
|
+
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
121
|
+
if (requestType === 'DeviceInfoGet') {
|
|
122
|
+
return {
|
|
123
|
+
message: {
|
|
124
|
+
protocol_version: 2,
|
|
125
|
+
hw: { serial_no: 'SERIAL-1' },
|
|
126
|
+
fw: { application: { version: '5.0.0' } },
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
131
|
+
return { message: protocolV2ApplicationInfo };
|
|
132
|
+
}
|
|
133
|
+
return { message: { init_states: true, unlocked: true } };
|
|
134
|
+
});
|
|
52
135
|
const device = createV2Device(typedCall);
|
|
53
136
|
|
|
54
137
|
const state = await device.getDeviceState();
|
|
55
138
|
|
|
56
139
|
expect(state.identity.serialNo).toBe('SERIAL-1');
|
|
57
|
-
expect(typedCall).toHaveBeenCalledTimes(
|
|
140
|
+
expect(typedCall).toHaveBeenCalledTimes(3);
|
|
58
141
|
expect(typedCall.mock.calls[0][0]).toBe('DeviceInfoGet');
|
|
59
142
|
expect(typedCall.mock.calls[0][2]?.targets?.status).not.toBe(true);
|
|
60
143
|
expect(typedCall).toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
|
|
61
144
|
});
|
|
62
145
|
|
|
63
146
|
test('uses one full DeviceInfoGet plus DeviceStatusGet when firmware refresh initializes state', async () => {
|
|
64
|
-
const typedCall = jest.fn().mockImplementation((requestType: string) =>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
147
|
+
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
148
|
+
if (requestType === 'DeviceInfoGet') {
|
|
149
|
+
return {
|
|
150
|
+
message: {
|
|
151
|
+
protocol_version: 2,
|
|
152
|
+
hw: { serial_no: 'SERIAL-1' },
|
|
153
|
+
fw: { application: { version: '5.0.0', hash: 'HASH-1' } },
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
158
|
+
return { message: protocolV2ApplicationInfo };
|
|
159
|
+
}
|
|
160
|
+
return { message: { init_states: true, unlocked: true } };
|
|
161
|
+
});
|
|
74
162
|
const device = createV2Device(typedCall);
|
|
75
163
|
|
|
76
164
|
await device.getDeviceState({
|
|
77
165
|
refreshSections: ['identity', 'versions', 'verification'],
|
|
78
166
|
});
|
|
79
167
|
|
|
80
|
-
expect(typedCall).toHaveBeenCalledTimes(
|
|
168
|
+
expect(typedCall).toHaveBeenCalledTimes(3);
|
|
81
169
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
82
170
|
1,
|
|
83
171
|
'DeviceInfoGet',
|
|
@@ -115,13 +203,13 @@ describe('getDeviceState', () => {
|
|
|
115
203
|
});
|
|
116
204
|
|
|
117
205
|
test.each(['bootloader', 'romloader'] as const)(
|
|
118
|
-
'
|
|
206
|
+
'uses ProtocolInfo to preserve %s mode without DeviceStatusGet',
|
|
119
207
|
async mode => {
|
|
120
208
|
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
121
209
|
if (requestType === 'DeviceInfoGet') {
|
|
122
210
|
return {
|
|
123
211
|
message: {
|
|
124
|
-
hw: { serial_no: 'SERIAL-1' },
|
|
212
|
+
hw: { Device_type: DeviceType.PRO2, serial_no: 'SERIAL-1' },
|
|
125
213
|
fw:
|
|
126
214
|
mode === 'romloader'
|
|
127
215
|
? { romloader: { version: '1.0.0' } }
|
|
@@ -129,27 +217,53 @@ describe('getDeviceState', () => {
|
|
|
129
217
|
},
|
|
130
218
|
};
|
|
131
219
|
}
|
|
132
|
-
|
|
220
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
221
|
+
return { message: getProtocolV2LoaderInfo(mode) };
|
|
222
|
+
}
|
|
223
|
+
throw new Error(`Unexpected request: ${requestType}`);
|
|
133
224
|
});
|
|
134
225
|
const device = createV2Device(typedCall);
|
|
135
|
-
device.updateState(
|
|
226
|
+
device.updateState(
|
|
227
|
+
{
|
|
228
|
+
protocol: 'V2',
|
|
229
|
+
identity: { deviceType: EDeviceType.Pro2 },
|
|
230
|
+
status: { mode },
|
|
231
|
+
raw: { protocolV2ProtocolInfo: getProtocolV2LoaderInfo(mode) },
|
|
232
|
+
},
|
|
233
|
+
'initialize'
|
|
234
|
+
);
|
|
136
235
|
|
|
137
236
|
const state = await device.getDeviceState({ refreshSections: ['status'] });
|
|
138
237
|
|
|
139
|
-
expect(state.status.mode).toBe(
|
|
140
|
-
expect(typedCall.
|
|
141
|
-
'DeviceInfoGet',
|
|
142
|
-
'DeviceStatusGet',
|
|
143
|
-
]);
|
|
238
|
+
expect(state.status.mode).toBe(mode);
|
|
239
|
+
expect(typedCall).not.toHaveBeenCalled();
|
|
144
240
|
}
|
|
145
241
|
);
|
|
146
242
|
|
|
147
243
|
test('refreshes Protocol V2 status only when explicitly requested in normal mode', async () => {
|
|
148
|
-
const typedCall = jest.fn().
|
|
149
|
-
|
|
244
|
+
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
245
|
+
if (requestType === 'DeviceInfoGet') {
|
|
246
|
+
return {
|
|
247
|
+
message: {
|
|
248
|
+
hw: { serial_no: 'SERIAL-1' },
|
|
249
|
+
fw: { application: { version: '5.0.0' } },
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
254
|
+
return { message: protocolV2ApplicationInfo };
|
|
255
|
+
}
|
|
256
|
+
return { message: { init_states: true, unlocked: true, device_id: 'device-1' } };
|
|
150
257
|
});
|
|
151
258
|
const device = createV2Device(typedCall);
|
|
152
|
-
device.updateState(
|
|
259
|
+
device.updateState(
|
|
260
|
+
{
|
|
261
|
+
protocol: 'V2',
|
|
262
|
+
status: { mode: 'normal' },
|
|
263
|
+
raw: { protocolV2ProtocolInfo: protocolV2ApplicationInfo },
|
|
264
|
+
},
|
|
265
|
+
'initialize'
|
|
266
|
+
);
|
|
153
267
|
|
|
154
268
|
const state = await device.getDeviceState({ refreshSections: ['status'] });
|
|
155
269
|
|
|
@@ -167,7 +281,14 @@ describe('getDeviceState', () => {
|
|
|
167
281
|
},
|
|
168
282
|
});
|
|
169
283
|
const device = createV2Device(typedCall);
|
|
170
|
-
device.updateState(
|
|
284
|
+
device.updateState(
|
|
285
|
+
{
|
|
286
|
+
protocol: 'V2',
|
|
287
|
+
status: { mode: 'normal' },
|
|
288
|
+
raw: { protocolV2ProtocolInfo: protocolV2ApplicationInfo },
|
|
289
|
+
},
|
|
290
|
+
'initialize'
|
|
291
|
+
);
|
|
171
292
|
const onState = jest.fn();
|
|
172
293
|
device.on(DEVICE.STATE, onState);
|
|
173
294
|
|
|
@@ -203,18 +324,27 @@ describe('getDeviceState', () => {
|
|
|
203
324
|
message: { init_states: true, unlocked: false, device_id: 'device-1' },
|
|
204
325
|
};
|
|
205
326
|
}
|
|
327
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
328
|
+
return { message: protocolV2ApplicationInfo };
|
|
329
|
+
}
|
|
206
330
|
if (requestType === 'DeviceSettingsGet') {
|
|
207
331
|
return { message: { label: 'Renamed Pro 2' } };
|
|
208
332
|
}
|
|
209
333
|
throw new Error(`Unexpected request: ${requestType}`);
|
|
210
334
|
});
|
|
211
335
|
const device = createV2Device(typedCall);
|
|
212
|
-
device.updateState(
|
|
336
|
+
device.updateState(
|
|
337
|
+
{
|
|
338
|
+
protocol: 'V2',
|
|
339
|
+
status: { mode: 'normal' },
|
|
340
|
+
raw: { protocolV2ProtocolInfo: protocolV2ApplicationInfo },
|
|
341
|
+
},
|
|
342
|
+
'initialize'
|
|
343
|
+
);
|
|
213
344
|
|
|
214
345
|
const state = await device.getDeviceState({ refreshSections: ['status', 'settings'] });
|
|
215
346
|
|
|
216
347
|
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
217
|
-
'DeviceInfoGet',
|
|
218
348
|
'DeviceStatusGet',
|
|
219
349
|
'DeviceSettingsGet',
|
|
220
350
|
]);
|
|
@@ -237,13 +367,20 @@ describe('getDeviceState', () => {
|
|
|
237
367
|
message: { init_states: true, unlocked: false, device_id: 'device-1' },
|
|
238
368
|
};
|
|
239
369
|
}
|
|
370
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
371
|
+
return { message: protocolV2ApplicationInfo };
|
|
372
|
+
}
|
|
240
373
|
throw Object.assign(new Error('Device locked'), {
|
|
241
374
|
errorCode: HardwareErrorCode.DeviceLocked,
|
|
242
375
|
});
|
|
243
376
|
});
|
|
244
377
|
const device = createV2Device(typedCall);
|
|
245
378
|
device.updateState(
|
|
246
|
-
{
|
|
379
|
+
{
|
|
380
|
+
protocol: 'V2',
|
|
381
|
+
status: { mode: 'normal', unlocked: true },
|
|
382
|
+
raw: { protocolV2ProtocolInfo: protocolV2ApplicationInfo },
|
|
383
|
+
},
|
|
247
384
|
'initialize'
|
|
248
385
|
);
|
|
249
386
|
|
|
@@ -383,7 +520,10 @@ describe('getDeviceState', () => {
|
|
|
383
520
|
},
|
|
384
521
|
};
|
|
385
522
|
}
|
|
386
|
-
|
|
523
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
524
|
+
return { message: getProtocolV2LoaderInfo('bootloader') };
|
|
525
|
+
}
|
|
526
|
+
throw new Error(`Unexpected request: ${requestType}`);
|
|
387
527
|
});
|
|
388
528
|
const device = createV2Device(typedCall);
|
|
389
529
|
device.updateState(
|