@onekeyfe/hd-core 1.2.0-alpha.106 → 1.2.0-alpha.107
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 +28 -100
- package/__tests__/device-lifecycle-events.test.ts +2 -113
- package/__tests__/device-pool-state.test.ts +0 -25
- package/__tests__/deviceSettings.test.ts +1 -0
- package/__tests__/deviceUploadNft.test.ts +4 -33
- package/__tests__/get-device-state.test.ts +13 -52
- package/__tests__/logBlockEvent.test.ts +1 -13
- package/__tests__/protocol-v2.test.ts +121 -758
- package/__tests__/refresh-device-state.test.ts +1 -3
- package/__tests__/search-devices.test.ts +38 -32
- package/dist/api/FirmwareUpdateV4.d.ts +3 -10
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts +1 -1
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +3 -2
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +3 -2
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +2 -7
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +16 -12
- package/dist/index.js +201 -446
- package/dist/protocols/protocol-v2/features.d.ts +1 -9
- 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/types/api/protocolV2.d.ts +1 -1
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/pro2Nft.d.ts +0 -7
- package/dist/utils/pro2Nft.d.ts.map +1 -1
- package/package.json +4 -6
- package/src/api/FirmwareUpdateV4.ts +174 -312
- package/src/api/SearchDevices.ts +2 -2
- package/src/api/UploadPortfolio.ts +2 -9
- package/src/api/protocol-v2/DeviceUploadNft.ts +8 -56
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +18 -37
- package/src/api/ton/TonSignMessage.ts +3 -5
- package/src/core/index.ts +2 -6
- package/src/device/Device.ts +31 -53
- package/src/device/DeviceCommands.ts +14 -4
- package/src/device/DevicePool.ts +2 -6
- package/src/events/logBlockEvent.ts +1 -14
- package/src/protocols/protocol-v2/features.ts +2 -19
- package/src/protocols/protocol-v2/index.ts +0 -2
- package/src/types/api/protocolV2.ts +1 -1
- package/src/types/device.ts +2 -1
- package/src/utils/deviceSettings.ts +1 -1
- package/src/utils/pro2Nft.ts +8 -31
- package/__tests__/base64Data.test.ts +0 -70
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +0 -145
- package/__tests__/resourceBase64Boundary.test.ts +0 -48
- package/__tests__/ton-sign-message.test.ts +0 -84
- package/dist/api/helpers/base64Data.d.ts +0 -16
- package/dist/api/helpers/base64Data.d.ts.map +0 -1
- package/src/api/helpers/base64Data.ts +0 -85
|
@@ -19,12 +19,12 @@ const createCommands = () => {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
describe('DeviceCommands failure mapping', () => {
|
|
22
|
-
it('logs passphrase request and
|
|
22
|
+
it('logs passphrase call request and response without exposing the passphrase', async () => {
|
|
23
23
|
const commands = createCommands();
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
const requestLog = getLogger(LoggerNames.DeviceCommands);
|
|
25
|
+
const responseLog = getLogger(LoggerNames.Core);
|
|
26
|
+
requestLog.messages.length = 0;
|
|
27
|
+
responseLog.messages.length = 0;
|
|
28
28
|
commands.mainId = 'main-id';
|
|
29
29
|
commands.transport = {
|
|
30
30
|
call: jest.fn().mockResolvedValue({
|
|
@@ -36,13 +36,13 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
36
36
|
} as any;
|
|
37
37
|
|
|
38
38
|
await expect(
|
|
39
|
-
commands.
|
|
39
|
+
commands.call('DeviceSessionAskPassphrase', {
|
|
40
40
|
passphrase: 'hidden-wallet-secret',
|
|
41
41
|
on_device: false,
|
|
42
42
|
})
|
|
43
43
|
).resolves.toMatchObject({ type: 'Success' });
|
|
44
44
|
|
|
45
|
-
expect(
|
|
45
|
+
expect(requestLog.messages.at(-1)?.message).toEqual([
|
|
46
46
|
'[DeviceCommands] [call] Sending',
|
|
47
47
|
'DeviceSessionAskPassphrase',
|
|
48
48
|
{
|
|
@@ -50,27 +50,21 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
50
50
|
on_device: false,
|
|
51
51
|
},
|
|
52
52
|
]);
|
|
53
|
-
expect(
|
|
54
|
-
'
|
|
53
|
+
expect(responseLog.messages.at(-1)?.message).toEqual([
|
|
54
|
+
'[DeviceCommands] [call] Received',
|
|
55
|
+
'Success',
|
|
55
56
|
{
|
|
56
|
-
|
|
57
|
-
response: {
|
|
58
|
-
type: 'Success',
|
|
59
|
-
message: {
|
|
60
|
-
message: 'Passphrase accepted',
|
|
61
|
-
},
|
|
62
|
-
},
|
|
57
|
+
message: 'Passphrase accepted',
|
|
63
58
|
},
|
|
64
59
|
]);
|
|
65
|
-
expect(
|
|
66
|
-
|
|
67
|
-
)
|
|
68
|
-
expect(JSON.stringify(log.messages)).not.toContain('hidden-wallet-secret');
|
|
60
|
+
expect(JSON.stringify([...requestLog.messages, ...responseLog.messages])).not.toContain(
|
|
61
|
+
'hidden-wallet-secret'
|
|
62
|
+
);
|
|
69
63
|
});
|
|
70
64
|
|
|
71
|
-
it('logs
|
|
65
|
+
it('logs DeviceStatus response fields without exposing the device ID', async () => {
|
|
72
66
|
const commands = createCommands();
|
|
73
|
-
const log = getLogger(LoggerNames.
|
|
67
|
+
const log = getLogger(LoggerNames.Core);
|
|
74
68
|
log.messages.length = 0;
|
|
75
69
|
commands.mainId = 'main-id';
|
|
76
70
|
commands.transport = {
|
|
@@ -88,27 +82,22 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
88
82
|
}),
|
|
89
83
|
} as any;
|
|
90
84
|
|
|
91
|
-
await expect(commands.
|
|
85
|
+
await expect(commands.call('DeviceStatusGet', {})).resolves.toMatchObject({
|
|
92
86
|
type: 'DeviceStatus',
|
|
93
87
|
});
|
|
94
88
|
|
|
95
89
|
const receivedLog = log.messages.at(-1)?.message;
|
|
96
90
|
expect(receivedLog).toEqual([
|
|
97
|
-
'
|
|
91
|
+
'[DeviceCommands] [call] Received',
|
|
92
|
+
'DeviceStatus',
|
|
98
93
|
{
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
backup_required: false,
|
|
107
|
-
passphrase_enabled: true,
|
|
108
|
-
attach_to_pin_enabled: false,
|
|
109
|
-
unlocked_by_attach_to_pin: false,
|
|
110
|
-
},
|
|
111
|
-
},
|
|
94
|
+
device_id: '[REDACTED]',
|
|
95
|
+
unlocked: true,
|
|
96
|
+
init_states: true,
|
|
97
|
+
backup_required: false,
|
|
98
|
+
passphrase_enabled: true,
|
|
99
|
+
attach_to_pin_enabled: false,
|
|
100
|
+
unlocked_by_attach_to_pin: false,
|
|
112
101
|
},
|
|
113
102
|
]);
|
|
114
103
|
expect(JSON.stringify(receivedLog)).not.toContain('sensitive-device-id');
|
|
@@ -136,59 +125,6 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
136
125
|
expect(JSON.stringify(log.messages)).not.toContain('secret-wallet-address');
|
|
137
126
|
});
|
|
138
127
|
|
|
139
|
-
it('logs the sanitized DeviceInfo response payload', async () => {
|
|
140
|
-
const commands = createCommands();
|
|
141
|
-
const log = getLogger(LoggerNames.DeviceCommands);
|
|
142
|
-
log.messages.length = 0;
|
|
143
|
-
|
|
144
|
-
await expect(
|
|
145
|
-
commands._filterCommonTypes(
|
|
146
|
-
{
|
|
147
|
-
type: 'DeviceInfo',
|
|
148
|
-
message: {
|
|
149
|
-
protocol_version: 2,
|
|
150
|
-
hw: {
|
|
151
|
-
Device_type: 7,
|
|
152
|
-
hardware_version: '1.0.0',
|
|
153
|
-
},
|
|
154
|
-
fw: {
|
|
155
|
-
application: {
|
|
156
|
-
version: '5.0.0',
|
|
157
|
-
build_id: '20260811',
|
|
158
|
-
hash: 'firmware-hash',
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
} as any,
|
|
163
|
-
'DeviceInfoGet'
|
|
164
|
-
)
|
|
165
|
-
).resolves.toMatchObject({ type: 'DeviceInfo' });
|
|
166
|
-
|
|
167
|
-
expect(log.messages.at(-1)?.message).toEqual([
|
|
168
|
-
'_filterCommonTypes: ',
|
|
169
|
-
{
|
|
170
|
-
request: 'DeviceInfoGet',
|
|
171
|
-
response: {
|
|
172
|
-
type: 'DeviceInfo',
|
|
173
|
-
message: {
|
|
174
|
-
protocol_version: 2,
|
|
175
|
-
hw: {
|
|
176
|
-
Device_type: 7,
|
|
177
|
-
hardware_version: '1.0.0',
|
|
178
|
-
},
|
|
179
|
-
fw: {
|
|
180
|
-
application: {
|
|
181
|
-
version: '5.0.0',
|
|
182
|
-
build_id: '20260811',
|
|
183
|
-
hash: 'firmware-hash',
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
|
-
]);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
128
|
it('logs the sanitized DeviceFirmwareUpdateStatus response payload', async () => {
|
|
193
129
|
const commands = createCommands();
|
|
194
130
|
const log = getLogger(LoggerNames.DeviceCommands);
|
|
@@ -625,22 +561,17 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
625
561
|
});
|
|
626
562
|
|
|
627
563
|
describe('DeviceCommands Protocol V2 interactive response compatibility', () => {
|
|
628
|
-
it('auto-acks
|
|
564
|
+
it('still auto-acks ButtonRequest when no hardware UI listener is registered', async () => {
|
|
629
565
|
const commands = createCommands();
|
|
630
566
|
const emit = jest.fn();
|
|
631
|
-
const cancelDevice = jest.spyOn(commands, 'cancelDevice').mockResolvedValue(undefined);
|
|
632
|
-
const cancelDeviceOnOneKeyDevice = jest
|
|
633
|
-
.spyOn(commands, 'cancelDeviceOnOneKeyDevice')
|
|
634
|
-
.mockResolvedValue(undefined);
|
|
635
567
|
const commonCall = jest.fn().mockResolvedValue({
|
|
636
568
|
type: 'Success',
|
|
637
569
|
message: {},
|
|
638
570
|
});
|
|
639
|
-
const setCancelableAction = jest.fn();
|
|
640
571
|
commands.device = {
|
|
641
572
|
...commands.device,
|
|
642
573
|
getCurrentDeviceType: jest.fn(() => 'pro2'),
|
|
643
|
-
setCancelableAction,
|
|
574
|
+
setCancelableAction: jest.fn(),
|
|
644
575
|
emit,
|
|
645
576
|
listenerCount: jest.fn(() => 0),
|
|
646
577
|
} as any;
|
|
@@ -662,9 +593,6 @@ describe('DeviceCommands Protocol V2 interactive response compatibility', () =>
|
|
|
662
593
|
expect.objectContaining({ code: 'ButtonRequest_PinEntry' })
|
|
663
594
|
);
|
|
664
595
|
expect(commonCall).toHaveBeenCalledWith('ButtonAck', {}, undefined);
|
|
665
|
-
await setCancelableAction.mock.calls[0][0]();
|
|
666
|
-
expect(cancelDevice).toHaveBeenCalledTimes(1);
|
|
667
|
-
expect(cancelDeviceOnOneKeyDevice).not.toHaveBeenCalled();
|
|
668
596
|
});
|
|
669
597
|
|
|
670
598
|
it('rejects PassphraseRequest when the Pro2 UI listener is not registered', async () => {
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
|
|
3
3
|
|
|
4
4
|
import { initConnector, initCore } from '../src/core';
|
|
5
5
|
import { DataManager } from '../src/data-manager';
|
|
6
6
|
import TransportManager from '../src/data-manager/TransportManager';
|
|
7
7
|
import { Device } from '../src/device/Device';
|
|
8
|
-
import { cancelDeviceInPrompt, cancelDeviceWithInitialize } from '../src/device/DeviceCommands';
|
|
9
8
|
import { DevicePool } from '../src/device/DevicePool';
|
|
10
9
|
import { CORE_EVENT, DEVICE, IFRAME } from '../src/events';
|
|
11
|
-
import { PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE } from '../src/protocols/protocol-v2';
|
|
12
10
|
|
|
13
11
|
import type Core from '../src/core';
|
|
14
12
|
import type { CoreMessage } from '../src/events';
|
|
@@ -273,115 +271,6 @@ describe('public device lifecycle events', () => {
|
|
|
273
271
|
expect(device.isUsedHere()).toBe(false);
|
|
274
272
|
});
|
|
275
273
|
|
|
276
|
-
test.each([
|
|
277
|
-
['V2', 'Cancel', 'webusb'],
|
|
278
|
-
['V2', 'Cancel', 'desktop-web-ble'],
|
|
279
|
-
['V2', 'Cancel', 'react-native'],
|
|
280
|
-
['V1', 'Initialize', 'webusb'],
|
|
281
|
-
['V1', 'Cancel', 'desktop-web-ble'],
|
|
282
|
-
] as const)(
|
|
283
|
-
'sends Protocol %s %s without treating the acquired %s session as disconnected',
|
|
284
|
-
async (protocol, messageType, env) => {
|
|
285
|
-
jest.spyOn(DataManager, 'getSettings').mockReturnValue(env as never);
|
|
286
|
-
const device = createInitializedDevice(protocol);
|
|
287
|
-
const post = jest.fn().mockResolvedValue(undefined);
|
|
288
|
-
const call = jest.fn().mockResolvedValue(undefined);
|
|
289
|
-
const cancel = jest.fn().mockResolvedValue(undefined);
|
|
290
|
-
device.originalDescriptor.session = device.mainId;
|
|
291
|
-
(device as unknown as { deviceAcquired: boolean }).deviceAcquired = true;
|
|
292
|
-
(device as any).commands = { transport: { post, call }, cancel };
|
|
293
|
-
device.setCancelableAction(() =>
|
|
294
|
-
messageType === 'Cancel'
|
|
295
|
-
? cancelDeviceInPrompt(device, false)
|
|
296
|
-
: cancelDeviceWithInitialize(device)
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
await device.interruptionFromUser();
|
|
300
|
-
|
|
301
|
-
if (messageType === 'Cancel') {
|
|
302
|
-
expect(post).toHaveBeenCalledWith(device.mainId, 'Cancel', {});
|
|
303
|
-
expect(call).not.toHaveBeenCalled();
|
|
304
|
-
} else {
|
|
305
|
-
expect(call).toHaveBeenCalledWith(device.mainId, 'Initialize', {});
|
|
306
|
-
expect(post).not.toHaveBeenCalled();
|
|
307
|
-
}
|
|
308
|
-
expect(cancel).toHaveBeenCalledTimes(1);
|
|
309
|
-
expect(device.hasDeviceAcquire()).toBe(true);
|
|
310
|
-
}
|
|
311
|
-
);
|
|
312
|
-
|
|
313
|
-
test.each([
|
|
314
|
-
[EDeviceType.Pro2, 'webusb', false, DeviceType.PRO2],
|
|
315
|
-
[EDeviceType.Neo, 'webusb', false, DeviceType.NEO],
|
|
316
|
-
[EDeviceType.Pro2, 'desktop-web-ble', true, DeviceType.PRO2],
|
|
317
|
-
[EDeviceType.Neo, 'desktop-web-ble', true, DeviceType.NEO],
|
|
318
|
-
[EDeviceType.Pro2, 'react-native', true, DeviceType.PRO2],
|
|
319
|
-
[EDeviceType.Neo, 'react-native', true, DeviceType.NEO],
|
|
320
|
-
[EDeviceType.Pro2, 'lowlevel', false, DeviceType.PRO2],
|
|
321
|
-
] as const)(
|
|
322
|
-
'refreshes %s runtime state after acquiring a fresh %s session',
|
|
323
|
-
async (deviceType, env, isBle, protocolV2DeviceType) => {
|
|
324
|
-
jest.spyOn(DataManager, 'getSettings').mockReturnValue(env as never);
|
|
325
|
-
const device = createInitializedDevice('V2');
|
|
326
|
-
const staleProtocolInfo = {
|
|
327
|
-
version: 1,
|
|
328
|
-
build_fingerprint: 'bootloader__1.0.0__abcdef0__PROD__RELEASE',
|
|
329
|
-
supported_messages: [],
|
|
330
|
-
};
|
|
331
|
-
const freshProtocolInfo = {
|
|
332
|
-
version: 1,
|
|
333
|
-
build_fingerprint: 'application__5.0.0__abcdef0__PROD__RELEASE',
|
|
334
|
-
supported_messages: [PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE],
|
|
335
|
-
};
|
|
336
|
-
device.updateState(
|
|
337
|
-
{
|
|
338
|
-
identity: { deviceType },
|
|
339
|
-
status: { mode: 'bootloader' },
|
|
340
|
-
raw: { protocolV2ProtocolInfo: staleProtocolInfo },
|
|
341
|
-
},
|
|
342
|
-
'initialize'
|
|
343
|
-
);
|
|
344
|
-
const acquire = jest
|
|
345
|
-
.fn()
|
|
346
|
-
.mockResolvedValue(isBle ? { uuid: 'fresh-session', protocolType: 'V2' } : 'fresh-session');
|
|
347
|
-
device.deviceConnector = { acquire } as never;
|
|
348
|
-
|
|
349
|
-
await device.acquire('V2', { throwOnRunPromiseError: true });
|
|
350
|
-
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
351
|
-
if (requestType === 'DeviceInfoGet') {
|
|
352
|
-
return {
|
|
353
|
-
message: {
|
|
354
|
-
protocol_version: 2,
|
|
355
|
-
hw: { Device_type: protocolV2DeviceType, serial_no: 'SERIAL-001' },
|
|
356
|
-
fw: { application: { version: '5.0.0' } },
|
|
357
|
-
},
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
if (requestType === 'ProtocolInfoRequest') {
|
|
361
|
-
return { message: freshProtocolInfo };
|
|
362
|
-
}
|
|
363
|
-
if (requestType === 'DeviceStatusGet') {
|
|
364
|
-
return {
|
|
365
|
-
message: { init_states: true, unlocked: true, device_id: 'wallet-device-id' },
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
throw new Error(`Unexpected request: ${requestType}`);
|
|
369
|
-
});
|
|
370
|
-
device.commands.typedCall = typedCall as never;
|
|
371
|
-
|
|
372
|
-
await device.initialize();
|
|
373
|
-
|
|
374
|
-
expect(typedCall.mock.calls.map(callArgs => callArgs[0])).toEqual([
|
|
375
|
-
'DeviceInfoGet',
|
|
376
|
-
'ProtocolInfoRequest',
|
|
377
|
-
'DeviceStatusGet',
|
|
378
|
-
]);
|
|
379
|
-
expect(device.state?.status.mode).toBe('normal');
|
|
380
|
-
expect(device.state?.identity.deviceType).toBe(deviceType);
|
|
381
|
-
expect(device.hasDeviceAcquire()).toBe(true);
|
|
382
|
-
}
|
|
383
|
-
);
|
|
384
|
-
|
|
385
274
|
test('exposes the current transport usage state in public device snapshots', () => {
|
|
386
275
|
const getSettings = jest
|
|
387
276
|
.spyOn(DataManager, 'getSettings')
|
|
@@ -38,31 +38,6 @@ describe('DevicePool state lifecycle', () => {
|
|
|
38
38
|
expect(getDeviceState).toHaveBeenNthCalledWith(2, { refreshSections: ['settings'] });
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
test('refreshes Protocol V2 discovery state without legacy-specific options', async () => {
|
|
42
|
-
const getDeviceState = jest.fn().mockResolvedValue({ status: { mode: 'bootloader' } });
|
|
43
|
-
const run = jest.fn(async (callback: () => Promise<void>) => callback());
|
|
44
|
-
const device = {
|
|
45
|
-
isProtocolV2: () => true,
|
|
46
|
-
getDeviceState,
|
|
47
|
-
run,
|
|
48
|
-
} as any;
|
|
49
|
-
|
|
50
|
-
await (DevicePool as any)._refreshRuntimeState(device, {
|
|
51
|
-
refreshRuntimeState: true,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
expect(run).toHaveBeenCalledWith(expect.any(Function), {
|
|
55
|
-
connectProtocol: undefined,
|
|
56
|
-
forceProtocolDetection: undefined,
|
|
57
|
-
});
|
|
58
|
-
expect(getDeviceState).toHaveBeenNthCalledWith(1, {
|
|
59
|
-
refreshSections: ['status'],
|
|
60
|
-
});
|
|
61
|
-
expect(getDeviceState).toHaveBeenNthCalledWith(2, {
|
|
62
|
-
refreshSections: ['settings'],
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
41
|
test('actively re-detects a cached device when protocol detection is forced', async () => {
|
|
67
42
|
const descriptor = { path: 'cached-path', protocolType: 'V1' } as any;
|
|
68
43
|
const device = Device.fromDescriptor(descriptor);
|
|
@@ -67,6 +67,7 @@ describe('Pro 2 device settings options', () => {
|
|
|
67
67
|
{ label: '2 minutes', valueMs: 120_000 },
|
|
68
68
|
{ label: '5 minutes', valueMs: 300_000 },
|
|
69
69
|
{ label: '10 minutes', valueMs: 600_000 },
|
|
70
|
+
{ label: '30 minutes', valueMs: 1_800_000 },
|
|
70
71
|
{ label: 'Never', valueMs: PROTOCOL_V2_NEVER_TIMEOUT_MS },
|
|
71
72
|
]);
|
|
72
73
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
-
import { encode as encodeJpeg } from 'jpeg-js';
|
|
3
2
|
|
|
4
3
|
import DeviceUploadNft from '../src/api/protocol-v2/DeviceUploadNft';
|
|
5
4
|
|
|
@@ -14,19 +13,6 @@ const createRgba = (width: number, height: number) => {
|
|
|
14
13
|
return data;
|
|
15
14
|
};
|
|
16
15
|
|
|
17
|
-
const jpegBase64Cache = new Map<string, string>();
|
|
18
|
-
|
|
19
|
-
const createJpegBase64 = (width: number, height: number) => {
|
|
20
|
-
const key = `${width}x${height}`;
|
|
21
|
-
const cached = jpegBase64Cache.get(key);
|
|
22
|
-
if (cached) return cached;
|
|
23
|
-
const value = encodeJpeg({ width, height, data: createRgba(width, height) }, 80).data.toString(
|
|
24
|
-
'base64'
|
|
25
|
-
);
|
|
26
|
-
jpegBase64Cache.set(key, value);
|
|
27
|
-
return value;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
16
|
const createMethod = ({
|
|
31
17
|
typedCall,
|
|
32
18
|
supportedMessages = [60802, 60805, 60808, 61500],
|
|
@@ -40,8 +26,8 @@ const createMethod = ({
|
|
|
40
26
|
id: 1,
|
|
41
27
|
payload: {
|
|
42
28
|
method: 'deviceUploadNft',
|
|
43
|
-
|
|
44
|
-
|
|
29
|
+
image: { width: 540, height: 540, rgba: createRgba(540, 540) },
|
|
30
|
+
thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
|
|
45
31
|
title: 'CryptoPunk #3100',
|
|
46
32
|
subtitle: 'CryptoPunks',
|
|
47
33
|
timestampMs: 1_760_000_000_000,
|
|
@@ -100,8 +86,8 @@ describe('DeviceUploadNft', () => {
|
|
|
100
86
|
id: 1,
|
|
101
87
|
payload: {
|
|
102
88
|
method: 'deviceUploadNft',
|
|
103
|
-
|
|
104
|
-
|
|
89
|
+
image: { width: 540, height: 540, rgba: createRgba(540, 540) },
|
|
90
|
+
thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
|
|
105
91
|
title: 'CryptoPunk #3100',
|
|
106
92
|
subtitle: 'CryptoPunks',
|
|
107
93
|
timestampMs: 1_760_000_000_000,
|
|
@@ -116,21 +102,6 @@ describe('DeviceUploadNft', () => {
|
|
|
116
102
|
});
|
|
117
103
|
});
|
|
118
104
|
|
|
119
|
-
test('rejects invalid image Base64 before device communication', () => {
|
|
120
|
-
const method = new DeviceUploadNft({
|
|
121
|
-
id: 1,
|
|
122
|
-
payload: {
|
|
123
|
-
method: 'deviceUploadNft',
|
|
124
|
-
imageJpegBase64: 'not-base64',
|
|
125
|
-
thumbnailJpegBase64: createJpegBase64(263, 263),
|
|
126
|
-
title: 'NFT',
|
|
127
|
-
subtitle: '',
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
expect(() => method.init()).toThrow('canonical Base64');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
105
|
test('uploads the triplet in order without creating the firmware-owned directory', async () => {
|
|
135
106
|
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
136
107
|
if (request === 'FilesystemPathInfoQuery') {
|
|
@@ -202,61 +202,25 @@ describe('getDeviceState', () => {
|
|
|
202
202
|
expect(state.versions.se01).toBe('1.0.0');
|
|
203
203
|
});
|
|
204
204
|
|
|
205
|
-
test.each([
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
['bootloader', EDeviceType.Neo, DeviceType.NEO],
|
|
209
|
-
['romloader', EDeviceType.Neo, DeviceType.NEO],
|
|
210
|
-
] as const)(
|
|
211
|
-
'refreshes cached %s state after %s reboots into the application',
|
|
212
|
-
async (mode, deviceType, protocolV2DeviceType) => {
|
|
205
|
+
test.each(['bootloader', 'romloader'] as const)(
|
|
206
|
+
'uses ProtocolInfo to preserve %s mode without DeviceStatusGet',
|
|
207
|
+
async mode => {
|
|
213
208
|
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
214
|
-
if (requestType === '
|
|
215
|
-
return { message: protocolV2ApplicationInfo };
|
|
216
|
-
}
|
|
217
|
-
if (requestType === 'DeviceStatusGet') {
|
|
209
|
+
if (requestType === 'DeviceInfoGet') {
|
|
218
210
|
return {
|
|
219
|
-
message: {
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
throw new Error(`Unexpected request: ${requestType}`);
|
|
223
|
-
});
|
|
224
|
-
const device = createV2Device(typedCall);
|
|
225
|
-
device.updateState(
|
|
226
|
-
{
|
|
227
|
-
protocol: 'V2',
|
|
228
|
-
identity: { deviceType },
|
|
229
|
-
status: { mode },
|
|
230
|
-
raw: {
|
|
231
|
-
protocolV2DeviceInfo: {
|
|
232
|
-
hw: { Device_type: protocolV2DeviceType, serial_no: 'SERIAL-1' },
|
|
211
|
+
message: {
|
|
212
|
+
hw: { Device_type: DeviceType.PRO2, serial_no: 'SERIAL-1' },
|
|
233
213
|
fw:
|
|
234
214
|
mode === 'romloader'
|
|
235
215
|
? { romloader: { version: '1.0.0' } }
|
|
236
216
|
: { bootloader: { version: '1.0.0' } },
|
|
237
217
|
},
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const state = await device.getDeviceState({ refreshSections: ['status'] });
|
|
245
|
-
|
|
246
|
-
expect(state.status.mode).toBe('normal');
|
|
247
|
-
expect(state.identity.deviceId).toBe('wallet-1');
|
|
248
|
-
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
249
|
-
'ProtocolInfoRequest',
|
|
250
|
-
'DeviceStatusGet',
|
|
251
|
-
]);
|
|
252
|
-
}
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
test.each(['bootloader', 'romloader'] as const)(
|
|
256
|
-
'keeps live %s mode after refreshing cached loader state',
|
|
257
|
-
async mode => {
|
|
258
|
-
const typedCall = jest.fn().mockResolvedValue({
|
|
259
|
-
message: getProtocolV2LoaderInfo(mode),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
221
|
+
return { message: getProtocolV2LoaderInfo(mode) };
|
|
222
|
+
}
|
|
223
|
+
throw new Error(`Unexpected request: ${requestType}`);
|
|
260
224
|
});
|
|
261
225
|
const device = createV2Device(typedCall);
|
|
262
226
|
device.updateState(
|
|
@@ -272,10 +236,7 @@ describe('getDeviceState', () => {
|
|
|
272
236
|
const state = await device.getDeviceState({ refreshSections: ['status'] });
|
|
273
237
|
|
|
274
238
|
expect(state.status.mode).toBe(mode);
|
|
275
|
-
expect(typedCall).
|
|
276
|
-
expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
277
|
-
eventless_wallet_session: true,
|
|
278
|
-
});
|
|
239
|
+
expect(typedCall).not.toHaveBeenCalled();
|
|
279
240
|
}
|
|
280
241
|
);
|
|
281
242
|
|
|
@@ -104,19 +104,7 @@ describe('getLogBlockLabel', () => {
|
|
|
104
104
|
});
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
it.each(['deviceUploadNft', 'deviceUploadWallpaper', 'uploadPortfolio'])(
|
|
108
|
-
'skips large Base64 resource payload logging for %s',
|
|
109
|
-
method => {
|
|
110
|
-
const request = { method, path: 'resource.bin', data: 'A'.repeat(1024 * 1024) };
|
|
111
|
-
expect(getLogBlockLabel(request)).toBe(method);
|
|
112
|
-
expect(getSafeLogPayload(request, method)).toEqual({
|
|
113
|
-
method,
|
|
114
|
-
payload: '[REDACTED]',
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
it.each(['fileWrite', 'fileRead'])(
|
|
107
|
+
it.each(['deviceUploadNft', 'deviceUploadWallpaper', 'uploadPortfolio', 'fileWrite', 'fileRead'])(
|
|
120
108
|
'keeps metadata and replaces binary payloads with their size for %s',
|
|
121
109
|
method => {
|
|
122
110
|
const request = { method, path: 'resource.bin', data: new Uint8Array(1024) };
|