@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,10 +1,11 @@
|
|
|
1
1
|
import { EDeviceType, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
-
import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
|
|
2
|
+
import { DeviceSessionPinType, DeviceSessionSeedDomain } from '@onekeyfe/hd-transport';
|
|
3
3
|
|
|
4
4
|
import GetPassphraseState from '../src/api/GetPassphraseState';
|
|
5
5
|
import OpenWalletSession from '../src/api/OpenWalletSession';
|
|
6
6
|
import { Device } from '../src/device/Device';
|
|
7
7
|
import { deviceWalletSessionStore } from '../src/device/DeviceWalletSessionStore';
|
|
8
|
+
import { ensureProtocolV2WalletSessionUnlocked } from '../src/protocols/protocol-v2/walletSession';
|
|
8
9
|
|
|
9
10
|
jest.mock('../src/data/config', () => ({
|
|
10
11
|
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
@@ -43,6 +44,11 @@ const createDevice = ({
|
|
|
43
44
|
}),
|
|
44
45
|
promptPassphrase,
|
|
45
46
|
},
|
|
47
|
+
ensureProtocolV2RuntimeContext: jest.fn(() =>
|
|
48
|
+
device.commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
49
|
+
eventless_wallet_session: true,
|
|
50
|
+
})
|
|
51
|
+
),
|
|
46
52
|
getDeviceState: jest.fn().mockResolvedValue({
|
|
47
53
|
identity: { deviceId: 'device-1' },
|
|
48
54
|
status: { passphraseProtection },
|
|
@@ -82,6 +88,50 @@ describe('openWalletSession', () => {
|
|
|
82
88
|
deviceWalletSessionStore.clear();
|
|
83
89
|
});
|
|
84
90
|
|
|
91
|
+
test('unlocks a locked Protocol V2 device before restoring a wallet session', async () => {
|
|
92
|
+
const device = {
|
|
93
|
+
commands: {
|
|
94
|
+
typedCall: jest.fn().mockResolvedValue({ message: { unlocked: false } }),
|
|
95
|
+
},
|
|
96
|
+
isBootloader: jest.fn().mockReturnValue(false),
|
|
97
|
+
isProtocolV2: jest.fn().mockReturnValue(true),
|
|
98
|
+
isRomloader: jest.fn().mockReturnValue(false),
|
|
99
|
+
state: { status: { unlocked: false } },
|
|
100
|
+
unlockDevice: jest.fn().mockResolvedValue(undefined),
|
|
101
|
+
updateProtocolV2Status: jest.fn(function updateProtocolV2Status(
|
|
102
|
+
this: { state: { status: { unlocked: boolean } } },
|
|
103
|
+
status: { unlocked?: boolean }
|
|
104
|
+
) {
|
|
105
|
+
this.state.status.unlocked = status.unlocked ?? this.state.status.unlocked;
|
|
106
|
+
}),
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
await expect(ensureProtocolV2WalletSessionUnlocked(device as any)).resolves.toBe(true);
|
|
110
|
+
expect(device.commands.typedCall).toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
|
|
111
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
|
|
112
|
+
source: 'unlock-coordinator',
|
|
113
|
+
reason: 'device-locked',
|
|
114
|
+
deviceOnly: true,
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('does not request PIN when wallet-session recovery finds the device unlocked', async () => {
|
|
119
|
+
const device = {
|
|
120
|
+
commands: {
|
|
121
|
+
typedCall: jest.fn().mockResolvedValue({ message: { unlocked: true } }),
|
|
122
|
+
},
|
|
123
|
+
isBootloader: jest.fn().mockReturnValue(false),
|
|
124
|
+
isProtocolV2: jest.fn().mockReturnValue(true),
|
|
125
|
+
isRomloader: jest.fn().mockReturnValue(false),
|
|
126
|
+
state: { status: { unlocked: true } },
|
|
127
|
+
unlockDevice: jest.fn(),
|
|
128
|
+
updateProtocolV2Status: jest.fn(),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
await expect(ensureProtocolV2WalletSessionUnlocked(device as any)).resolves.toBe(false);
|
|
132
|
+
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
|
|
85
135
|
test.each([{ useEmptyPassphrase: true }, { initSession: true }, {}])(
|
|
86
136
|
'requires the explicit mode in the new public API: %p',
|
|
87
137
|
legacyParams => {
|
|
@@ -108,7 +158,7 @@ describe('openWalletSession', () => {
|
|
|
108
158
|
session_id: 'pro2-wallet-session',
|
|
109
159
|
},
|
|
110
160
|
});
|
|
111
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
161
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
112
162
|
const method = new GetPassphraseState({
|
|
113
163
|
payload: {
|
|
114
164
|
method: 'getPassphraseState',
|
|
@@ -130,7 +180,8 @@ describe('openWalletSession', () => {
|
|
|
130
180
|
{ cancelDeviceOnReject: false }
|
|
131
181
|
);
|
|
132
182
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
133
|
-
|
|
183
|
+
passphrase: 'host hidden wallet',
|
|
184
|
+
on_device: false,
|
|
134
185
|
});
|
|
135
186
|
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
|
|
136
187
|
});
|
|
@@ -208,7 +259,7 @@ describe('openWalletSession', () => {
|
|
|
208
259
|
protocol: 'V1',
|
|
209
260
|
walletType: 'standard',
|
|
210
261
|
deviceId: 'device-1',
|
|
211
|
-
passphraseState:
|
|
262
|
+
passphraseState: null,
|
|
212
263
|
resumed: false,
|
|
213
264
|
});
|
|
214
265
|
expect(method.payload.useEmptyPassphrase).toBe(true);
|
|
@@ -229,7 +280,7 @@ describe('openWalletSession', () => {
|
|
|
229
280
|
session_id: 'hidden-session',
|
|
230
281
|
},
|
|
231
282
|
});
|
|
232
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
283
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
233
284
|
const method = new OpenWalletSession({
|
|
234
285
|
payload: {
|
|
235
286
|
method: 'openWalletSession',
|
|
@@ -247,11 +298,47 @@ describe('openWalletSession', () => {
|
|
|
247
298
|
});
|
|
248
299
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
249
300
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
250
|
-
|
|
301
|
+
passphrase: 'host hidden wallet',
|
|
302
|
+
on_device: false,
|
|
251
303
|
});
|
|
252
304
|
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
|
|
253
305
|
});
|
|
254
306
|
|
|
307
|
+
test('refreshes Pro2 status and unlocks before selecting a hidden wallet when locked', async () => {
|
|
308
|
+
const typedCall = jest
|
|
309
|
+
.fn()
|
|
310
|
+
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
311
|
+
.mockResolvedValueOnce({ message: {} })
|
|
312
|
+
.mockResolvedValueOnce({
|
|
313
|
+
message: {
|
|
314
|
+
btc_test_address: 'hidden-state-after-preflight',
|
|
315
|
+
session_id: 'hidden-session-after-preflight',
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
const method = new OpenWalletSession({
|
|
319
|
+
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
320
|
+
});
|
|
321
|
+
method.init();
|
|
322
|
+
const device = createDevice({
|
|
323
|
+
typedCall,
|
|
324
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphraseOnDevice: true }),
|
|
325
|
+
});
|
|
326
|
+
device.getDeviceState = jest.fn().mockResolvedValue({
|
|
327
|
+
identity: { deviceId: 'device-1' },
|
|
328
|
+
status: { unlocked: false, passphraseProtection: true },
|
|
329
|
+
});
|
|
330
|
+
method.device = device as any;
|
|
331
|
+
|
|
332
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
333
|
+
walletType: 'hidden',
|
|
334
|
+
passphraseState: 'hidden-state-after-preflight',
|
|
335
|
+
});
|
|
336
|
+
expect(device.unlockDevice).toHaveBeenCalledTimes(1);
|
|
337
|
+
expect(device.unlockDevice.mock.invocationCallOrder[0]).toBeLessThan(
|
|
338
|
+
typedCall.mock.invocationCallOrder[0]
|
|
339
|
+
);
|
|
340
|
+
});
|
|
341
|
+
|
|
255
342
|
test('classifies the selected Pro2 wallet from the post-unlock device state', async () => {
|
|
256
343
|
const typedCall = jest
|
|
257
344
|
.fn()
|
|
@@ -263,7 +350,7 @@ describe('openWalletSession', () => {
|
|
|
263
350
|
session_id: 'hidden-session-after-unlock',
|
|
264
351
|
},
|
|
265
352
|
});
|
|
266
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
353
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
267
354
|
const method = new OpenWalletSession({
|
|
268
355
|
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
269
356
|
});
|
|
@@ -275,10 +362,23 @@ describe('openWalletSession', () => {
|
|
|
275
362
|
});
|
|
276
363
|
device.features.unlocked = false;
|
|
277
364
|
device.getCurrentPassphraseProtection = () => device.features.passphraseProtection;
|
|
278
|
-
device.
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
365
|
+
device.commands.typedCall = jest.fn((request: string, ...args: unknown[]) => {
|
|
366
|
+
if (request === 'DeviceStatusGet') {
|
|
367
|
+
return {
|
|
368
|
+
message: {
|
|
369
|
+
device_id: 'device-1',
|
|
370
|
+
unlocked: true,
|
|
371
|
+
passphrase_enabled: true,
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
return typedCall(request, ...args);
|
|
376
|
+
});
|
|
377
|
+
device.updateProtocolV2Status = jest.fn((status: Record<string, unknown>) => {
|
|
378
|
+
device.features.unlocked = status.unlocked ?? device.features.unlocked;
|
|
379
|
+
device.features.passphraseProtection =
|
|
380
|
+
status.passphrase_enabled ?? device.features.passphraseProtection;
|
|
381
|
+
return device.features;
|
|
282
382
|
});
|
|
283
383
|
method.device = device as any;
|
|
284
384
|
|
|
@@ -293,6 +393,35 @@ describe('openWalletSession', () => {
|
|
|
293
393
|
expect(promptPassphrase).toHaveBeenCalledTimes(1);
|
|
294
394
|
});
|
|
295
395
|
|
|
396
|
+
test('rejects a Pro2 standard-wallet fallback after selecting a hidden wallet', async () => {
|
|
397
|
+
const typedCall = jest
|
|
398
|
+
.fn()
|
|
399
|
+
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
400
|
+
.mockResolvedValueOnce({ message: {} })
|
|
401
|
+
.mockResolvedValueOnce({
|
|
402
|
+
message: {
|
|
403
|
+
btc_test_address: 'standard-wallet-state',
|
|
404
|
+
session_id: 'standard-wallet-session',
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphraseOnDevice: true });
|
|
408
|
+
const method = new OpenWalletSession({
|
|
409
|
+
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
410
|
+
});
|
|
411
|
+
method.init();
|
|
412
|
+
const device = createDevice({
|
|
413
|
+
passphraseProtection: false,
|
|
414
|
+
typedCall,
|
|
415
|
+
promptPassphrase,
|
|
416
|
+
});
|
|
417
|
+
method.device = device as any;
|
|
418
|
+
|
|
419
|
+
await expect(method.run()).rejects.toMatchObject({
|
|
420
|
+
errorCode: HardwareErrorCode.DeviceNotOpenedPassphrase,
|
|
421
|
+
});
|
|
422
|
+
expect(device.clearInternalState).toHaveBeenCalled();
|
|
423
|
+
});
|
|
424
|
+
|
|
296
425
|
test('select-hidden starts a fresh hidden-wallet session on Protocol V2', async () => {
|
|
297
426
|
const typedCall = jest
|
|
298
427
|
.fn()
|
|
@@ -304,7 +433,7 @@ describe('openWalletSession', () => {
|
|
|
304
433
|
session_id: 'new-hidden-session',
|
|
305
434
|
},
|
|
306
435
|
});
|
|
307
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
436
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
308
437
|
const method = new OpenWalletSession({
|
|
309
438
|
payload: {
|
|
310
439
|
method: 'openWalletSession',
|
|
@@ -326,7 +455,8 @@ describe('openWalletSession', () => {
|
|
|
326
455
|
eventless_wallet_session: true,
|
|
327
456
|
});
|
|
328
457
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
329
|
-
|
|
458
|
+
passphrase: 'host hidden wallet',
|
|
459
|
+
on_device: false,
|
|
330
460
|
});
|
|
331
461
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
332
462
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
@@ -344,7 +474,7 @@ describe('openWalletSession', () => {
|
|
|
344
474
|
session_id: 'current-device-session',
|
|
345
475
|
},
|
|
346
476
|
});
|
|
347
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
477
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
348
478
|
const method = new OpenWalletSession({
|
|
349
479
|
payload: {
|
|
350
480
|
method: 'openWalletSession',
|
|
@@ -399,6 +529,7 @@ describe('openWalletSession', () => {
|
|
|
399
529
|
});
|
|
400
530
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
401
531
|
session_id: 'known-session',
|
|
532
|
+
btc_test_address: 'hidden-state',
|
|
402
533
|
});
|
|
403
534
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
404
535
|
});
|
|
@@ -569,14 +700,18 @@ describe('openWalletSession', () => {
|
|
|
569
700
|
protocol: 'V2',
|
|
570
701
|
walletType: 'standard',
|
|
571
702
|
deviceId: 'device-1',
|
|
572
|
-
passphraseState:
|
|
703
|
+
passphraseState: null,
|
|
573
704
|
resumed: false,
|
|
574
705
|
});
|
|
575
706
|
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
576
707
|
expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
577
708
|
eventless_wallet_session: true,
|
|
578
709
|
});
|
|
579
|
-
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main
|
|
710
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
|
|
711
|
+
source: 'wallet-session-coordinator',
|
|
712
|
+
reason: 'open-wallet',
|
|
713
|
+
deviceOnly: true,
|
|
714
|
+
});
|
|
580
715
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
581
716
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
582
717
|
expect(device.passphraseState).toBeUndefined();
|
|
@@ -602,7 +737,7 @@ describe('openWalletSession', () => {
|
|
|
602
737
|
.fn()
|
|
603
738
|
.mockResolvedValueOnce({
|
|
604
739
|
identity: { deviceId: undefined },
|
|
605
|
-
status: { passphraseProtection: true },
|
|
740
|
+
status: { unlocked: false, passphraseProtection: true },
|
|
606
741
|
})
|
|
607
742
|
.mockResolvedValueOnce({
|
|
608
743
|
identity: { deviceId: 'device-1' },
|
|
@@ -618,10 +753,14 @@ describe('openWalletSession', () => {
|
|
|
618
753
|
protocol: 'V2',
|
|
619
754
|
walletType: 'standard',
|
|
620
755
|
deviceId: 'device-1',
|
|
621
|
-
passphraseState:
|
|
756
|
+
passphraseState: null,
|
|
622
757
|
resumed: false,
|
|
623
758
|
});
|
|
624
|
-
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main
|
|
759
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
|
|
760
|
+
source: 'wallet-session-coordinator',
|
|
761
|
+
reason: 'open-wallet',
|
|
762
|
+
deviceOnly: true,
|
|
763
|
+
});
|
|
625
764
|
expect(device.getDeviceState).toHaveBeenCalledTimes(2);
|
|
626
765
|
});
|
|
627
766
|
|
|
@@ -641,6 +780,14 @@ describe('openWalletSession', () => {
|
|
|
641
780
|
method.init();
|
|
642
781
|
const device = createDevice({ typedCall });
|
|
643
782
|
device.features.unlockedAttachPin = true;
|
|
783
|
+
device.getDeviceState = jest.fn().mockResolvedValue({
|
|
784
|
+
identity: { deviceId: 'device-1' },
|
|
785
|
+
status: {
|
|
786
|
+
unlocked: true,
|
|
787
|
+
unlockedAttachPin: true,
|
|
788
|
+
passphraseProtection: true,
|
|
789
|
+
},
|
|
790
|
+
});
|
|
644
791
|
device.unlockDevice = jest.fn().mockImplementation(() => {
|
|
645
792
|
device.features.unlockedAttachPin = false;
|
|
646
793
|
return Promise.resolve(device.features);
|
|
@@ -649,9 +796,13 @@ describe('openWalletSession', () => {
|
|
|
649
796
|
|
|
650
797
|
await expect(method.run()).resolves.toMatchObject({
|
|
651
798
|
walletType: 'standard',
|
|
652
|
-
passphraseState:
|
|
799
|
+
passphraseState: null,
|
|
800
|
+
});
|
|
801
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
|
|
802
|
+
source: 'wallet-session-coordinator',
|
|
803
|
+
reason: 'open-wallet',
|
|
804
|
+
deviceOnly: true,
|
|
653
805
|
});
|
|
654
|
-
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main);
|
|
655
806
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
656
807
|
});
|
|
657
808
|
|
|
@@ -666,7 +817,7 @@ describe('openWalletSession', () => {
|
|
|
666
817
|
session_id: 'hidden-session',
|
|
667
818
|
},
|
|
668
819
|
});
|
|
669
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
820
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
670
821
|
const method = new OpenWalletSession({
|
|
671
822
|
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
672
823
|
});
|
|
@@ -683,7 +834,8 @@ describe('openWalletSession', () => {
|
|
|
683
834
|
resumed: false,
|
|
684
835
|
});
|
|
685
836
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
686
|
-
|
|
837
|
+
passphrase: 'host hidden wallet',
|
|
838
|
+
on_device: false,
|
|
687
839
|
});
|
|
688
840
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
689
841
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
@@ -691,7 +843,7 @@ describe('openWalletSession', () => {
|
|
|
691
843
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('hidden-session');
|
|
692
844
|
});
|
|
693
845
|
|
|
694
|
-
test('gets the prepared session after
|
|
846
|
+
test('gets the prepared session after host passphrase entry', async () => {
|
|
695
847
|
const typedCall = jest
|
|
696
848
|
.fn()
|
|
697
849
|
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
@@ -699,7 +851,7 @@ describe('openWalletSession', () => {
|
|
|
699
851
|
.mockResolvedValueOnce({
|
|
700
852
|
message: { btc_test_address: 'device-state', session_id: 'device-session' },
|
|
701
853
|
});
|
|
702
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
854
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
703
855
|
const method = new OpenWalletSession({
|
|
704
856
|
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
705
857
|
});
|
|
@@ -711,11 +863,37 @@ describe('openWalletSession', () => {
|
|
|
711
863
|
passphraseState: 'device-state',
|
|
712
864
|
});
|
|
713
865
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
714
|
-
|
|
866
|
+
passphrase: 'host hidden wallet',
|
|
867
|
+
on_device: false,
|
|
715
868
|
});
|
|
716
869
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
717
870
|
});
|
|
718
871
|
|
|
872
|
+
test('selects an on-device passphrase wallet with an explicit on_device request', async () => {
|
|
873
|
+
const typedCall = jest
|
|
874
|
+
.fn()
|
|
875
|
+
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
876
|
+
.mockResolvedValueOnce({ message: {} })
|
|
877
|
+
.mockResolvedValueOnce({
|
|
878
|
+
message: { btc_test_address: 'device-state', session_id: 'device-session' },
|
|
879
|
+
});
|
|
880
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphraseOnDevice: true });
|
|
881
|
+
const method = new OpenWalletSession({
|
|
882
|
+
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
883
|
+
});
|
|
884
|
+
method.init();
|
|
885
|
+
method.device = createDevice({ typedCall, promptPassphrase }) as any;
|
|
886
|
+
|
|
887
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
888
|
+
walletType: 'hidden',
|
|
889
|
+
passphraseState: 'device-state',
|
|
890
|
+
});
|
|
891
|
+
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
892
|
+
on_device: true,
|
|
893
|
+
});
|
|
894
|
+
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
|
|
895
|
+
});
|
|
896
|
+
|
|
719
897
|
test('forwards a host passphrase to Pro2 firmware', async () => {
|
|
720
898
|
const typedCall = jest
|
|
721
899
|
.fn()
|
|
@@ -832,7 +1010,9 @@ describe('openWalletSession', () => {
|
|
|
832
1010
|
},
|
|
833
1011
|
{ cancelDeviceOnReject: false }
|
|
834
1012
|
);
|
|
835
|
-
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.AttachToPin
|
|
1013
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.AttachToPin, {
|
|
1014
|
+
emitUiEvent: false,
|
|
1015
|
+
});
|
|
836
1016
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
837
1017
|
});
|
|
838
1018
|
|
|
@@ -910,7 +1090,7 @@ describe('openWalletSession', () => {
|
|
|
910
1090
|
.fn()
|
|
911
1091
|
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
912
1092
|
.mockRejectedValueOnce(new Error(message));
|
|
913
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
1093
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
914
1094
|
const method = new OpenWalletSession({
|
|
915
1095
|
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
916
1096
|
});
|
|
@@ -963,7 +1143,7 @@ describe('openWalletSession', () => {
|
|
|
963
1143
|
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
964
1144
|
});
|
|
965
1145
|
|
|
966
|
-
test('
|
|
1146
|
+
test('does not unlock or retry when wallet preparation reports a late DeviceLocked', async () => {
|
|
967
1147
|
const lockedError = { errorCode: HardwareErrorCode.DeviceLocked };
|
|
968
1148
|
const typedCall = jest
|
|
969
1149
|
.fn()
|
|
@@ -973,7 +1153,7 @@ describe('openWalletSession', () => {
|
|
|
973
1153
|
.mockResolvedValueOnce({
|
|
974
1154
|
message: { btc_test_address: 'hidden-state', session_id: 'hidden-session' },
|
|
975
1155
|
});
|
|
976
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
1156
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
977
1157
|
const method = new OpenWalletSession({
|
|
978
1158
|
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
979
1159
|
});
|
|
@@ -981,12 +1161,9 @@ describe('openWalletSession', () => {
|
|
|
981
1161
|
const device = createDevice({ typedCall, promptPassphrase });
|
|
982
1162
|
method.device = device as any;
|
|
983
1163
|
|
|
984
|
-
await expect(method.run()).
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
});
|
|
988
|
-
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main);
|
|
989
|
-
expect(typedCall).toHaveBeenCalledTimes(4);
|
|
1164
|
+
await expect(method.run()).rejects.toBe(lockedError);
|
|
1165
|
+
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
1166
|
+
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
990
1167
|
});
|
|
991
1168
|
|
|
992
1169
|
test('does not fall back when wallet preparation is rejected', async () => {
|
|
@@ -995,7 +1172,7 @@ describe('openWalletSession', () => {
|
|
|
995
1172
|
.fn()
|
|
996
1173
|
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
997
1174
|
.mockRejectedValueOnce(invalidSessionError);
|
|
998
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
1175
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
999
1176
|
const method = new OpenWalletSession({
|
|
1000
1177
|
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
1001
1178
|
});
|
|
@@ -1026,7 +1203,7 @@ describe('openWalletSession', () => {
|
|
|
1026
1203
|
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1027
1204
|
.mockResolvedValueOnce({ message: {} })
|
|
1028
1205
|
.mockResolvedValueOnce({ message });
|
|
1029
|
-
const promptPassphrase = jest.fn().mockResolvedValue({
|
|
1206
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
1030
1207
|
const method = new OpenWalletSession({
|
|
1031
1208
|
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
1032
1209
|
});
|
|
@@ -1076,6 +1253,7 @@ describe('openWalletSession', () => {
|
|
|
1076
1253
|
});
|
|
1077
1254
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1078
1255
|
session_id: 'known-session',
|
|
1256
|
+
btc_test_address: 'hidden-state',
|
|
1079
1257
|
});
|
|
1080
1258
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1081
1259
|
});
|
|
@@ -1107,7 +1285,7 @@ describe('openWalletSession', () => {
|
|
|
1107
1285
|
.fn()
|
|
1108
1286
|
.mockResolvedValueOnce({
|
|
1109
1287
|
identity: { deviceId: undefined },
|
|
1110
|
-
status: { passphraseProtection: true },
|
|
1288
|
+
status: { unlocked: false, passphraseProtection: true },
|
|
1111
1289
|
})
|
|
1112
1290
|
.mockResolvedValueOnce({
|
|
1113
1291
|
identity: { deviceId: 'device-1' },
|
|
@@ -1130,6 +1308,7 @@ describe('openWalletSession', () => {
|
|
|
1130
1308
|
expect(device.getDeviceState).toHaveBeenCalledTimes(2);
|
|
1131
1309
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1132
1310
|
session_id: 'known-session',
|
|
1311
|
+
btc_test_address: 'hidden-state',
|
|
1133
1312
|
});
|
|
1134
1313
|
const sessionGetCall = typedCall.mock.calls.findIndex(
|
|
1135
1314
|
([requestName]) => requestName === 'DeviceSessionGet'
|
|
@@ -1183,13 +1362,23 @@ describe('openWalletSession', () => {
|
|
|
1183
1362
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBeUndefined();
|
|
1184
1363
|
});
|
|
1185
1364
|
|
|
1186
|
-
test('
|
|
1187
|
-
const typedCall = jest
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1365
|
+
test('reselects the expected hidden wallet when a cached Protocol V2 session is invalid', async () => {
|
|
1366
|
+
const typedCall = jest
|
|
1367
|
+
.fn()
|
|
1368
|
+
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1369
|
+
.mockRejectedValueOnce(
|
|
1370
|
+
Object.assign(new Error('Invalid session'), {
|
|
1371
|
+
errorCode: HardwareErrorCode.WalletSessionInvalid,
|
|
1372
|
+
})
|
|
1373
|
+
)
|
|
1374
|
+
.mockResolvedValueOnce({ message: {} })
|
|
1375
|
+
.mockResolvedValueOnce({
|
|
1376
|
+
message: {
|
|
1377
|
+
btc_test_address: 'hidden-state',
|
|
1378
|
+
session_id: 'renewed-session',
|
|
1379
|
+
},
|
|
1380
|
+
});
|
|
1381
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
1193
1382
|
const method = new OpenWalletSession({
|
|
1194
1383
|
payload: {
|
|
1195
1384
|
method: 'openWalletSession',
|
|
@@ -1203,15 +1392,32 @@ describe('openWalletSession', () => {
|
|
|
1203
1392
|
deviceWalletSessionStore.set('device-1', 'hidden-state', 'expired-session');
|
|
1204
1393
|
method.device = createDevice({ typedCall, promptPassphrase }) as any;
|
|
1205
1394
|
|
|
1206
|
-
await expect(method.run()).
|
|
1207
|
-
|
|
1395
|
+
await expect(method.run()).resolves.toEqual({
|
|
1396
|
+
protocol: 'V2',
|
|
1397
|
+
walletType: 'hidden',
|
|
1398
|
+
deviceId: 'device-1',
|
|
1399
|
+
passphraseState: 'hidden-state',
|
|
1400
|
+
resumed: false,
|
|
1208
1401
|
});
|
|
1209
|
-
expect(
|
|
1402
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1403
|
+
session_id: 'expired-session',
|
|
1404
|
+
btc_test_address: 'hidden-state',
|
|
1405
|
+
});
|
|
1406
|
+
expect(promptPassphrase).toHaveBeenCalledTimes(1);
|
|
1407
|
+
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('renewed-session');
|
|
1210
1408
|
});
|
|
1211
1409
|
|
|
1212
|
-
test('
|
|
1213
|
-
const typedCall = jest
|
|
1214
|
-
|
|
1410
|
+
test('selects the expected hidden wallet when Protocol V2 has no cached session', async () => {
|
|
1411
|
+
const typedCall = jest
|
|
1412
|
+
.fn()
|
|
1413
|
+
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1414
|
+
.mockResolvedValueOnce({
|
|
1415
|
+
message: {
|
|
1416
|
+
btc_test_address: 'hidden-state',
|
|
1417
|
+
session_id: 'new-session',
|
|
1418
|
+
},
|
|
1419
|
+
});
|
|
1420
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
1215
1421
|
const method = new OpenWalletSession({
|
|
1216
1422
|
payload: {
|
|
1217
1423
|
method: 'openWalletSession',
|
|
@@ -1224,10 +1430,63 @@ describe('openWalletSession', () => {
|
|
|
1224
1430
|
method.init();
|
|
1225
1431
|
method.device = createDevice({ typedCall, promptPassphrase }) as any;
|
|
1226
1432
|
|
|
1227
|
-
await expect(method.run()).
|
|
1228
|
-
|
|
1433
|
+
await expect(method.run()).resolves.toEqual({
|
|
1434
|
+
protocol: 'V2',
|
|
1435
|
+
walletType: 'hidden',
|
|
1436
|
+
deviceId: 'device-1',
|
|
1437
|
+
passphraseState: 'hidden-state',
|
|
1438
|
+
resumed: false,
|
|
1439
|
+
});
|
|
1440
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1441
|
+
btc_test_address: 'hidden-state',
|
|
1229
1442
|
});
|
|
1230
|
-
expect(typedCall).not.toHaveBeenCalled();
|
|
1231
1443
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1444
|
+
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('new-session');
|
|
1445
|
+
});
|
|
1446
|
+
|
|
1447
|
+
test.each([
|
|
1448
|
+
{
|
|
1449
|
+
deriveCardano: false,
|
|
1450
|
+
seedDomains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1451
|
+
},
|
|
1452
|
+
{
|
|
1453
|
+
deriveCardano: true,
|
|
1454
|
+
seedDomains: [
|
|
1455
|
+
DeviceSessionSeedDomain.SeedDomain_Standard,
|
|
1456
|
+
DeviceSessionSeedDomain.SeedDomain_Cardano,
|
|
1457
|
+
],
|
|
1458
|
+
},
|
|
1459
|
+
])('requests the selected seed domains: $seedDomains', async ({ deriveCardano, seedDomains }) => {
|
|
1460
|
+
const typedCall = jest
|
|
1461
|
+
.fn()
|
|
1462
|
+
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1463
|
+
.mockResolvedValueOnce({ message: {} })
|
|
1464
|
+
.mockResolvedValueOnce({
|
|
1465
|
+
message: {
|
|
1466
|
+
btc_test_address: 'hidden-state',
|
|
1467
|
+
session_id: 'new-session',
|
|
1468
|
+
},
|
|
1469
|
+
});
|
|
1470
|
+
const method = new OpenWalletSession({
|
|
1471
|
+
payload: {
|
|
1472
|
+
method: 'openWalletSession',
|
|
1473
|
+
connectId: 'connect-id',
|
|
1474
|
+
mode: 'select-hidden',
|
|
1475
|
+
deriveCardano,
|
|
1476
|
+
},
|
|
1477
|
+
});
|
|
1478
|
+
method.init();
|
|
1479
|
+
method.device = createDevice({
|
|
1480
|
+
typedCall,
|
|
1481
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' }),
|
|
1482
|
+
}) as any;
|
|
1483
|
+
|
|
1484
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
1485
|
+
walletType: 'hidden',
|
|
1486
|
+
passphraseState: 'hidden-state',
|
|
1487
|
+
});
|
|
1488
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1489
|
+
seed_domains: seedDomains,
|
|
1490
|
+
});
|
|
1232
1491
|
});
|
|
1233
1492
|
});
|