@onekeyfe/hd-core 1.2.0-alpha.22 → 1.2.0-alpha.23
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 +8 -8
- package/__tests__/device-settings.test.ts +1 -1
- package/__tests__/device-wallet-session-store.test.ts +41 -0
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +86 -0
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +13 -0
- package/__tests__/logBlockEvent.test.ts +17 -0
- package/__tests__/open-wallet-session.test.ts +90 -31
- package/__tests__/protocol-v2.test.ts +301 -63
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/device/DeviceWipe.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -1
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +6 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +3 -2
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/events/device.d.ts +1 -0
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +1 -0
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +10 -6
- package/dist/index.js +157 -268
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/getPassphraseState.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.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/CheckAllFirmwareRelease.ts +15 -8
- package/src/api/FirmwareUpdateV2.ts +8 -1
- package/src/api/FirmwareUpdateV3.ts +8 -0
- package/src/api/FirmwareUpdateV4.ts +21 -19
- package/src/api/GetPassphraseState.ts +11 -5
- package/src/api/device/DeviceUpdateBootloader.ts +9 -1
- package/src/api/device/DeviceWipe.ts +2 -0
- package/src/api/firmware/FirmwareArtifactSource.ts +1 -1
- package/src/api/firmware/FirmwareUpdatePlan.ts +1 -1
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +15 -1
- package/src/core/index.ts +1 -0
- package/src/data/messages/messages-protocol-v2.json +18 -57
- package/src/device/Device.ts +30 -7
- package/src/device/DeviceCommands.ts +24 -151
- package/src/events/device.ts +1 -0
- package/src/events/logBlockEvent.ts +1 -15
- package/src/events/ui-request.ts +1 -0
- package/src/protocols/protocol-v2/deviceSession.ts +1 -1
- package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +0 -1
- package/src/protocols/protocol-v2/walletSession.ts +41 -20
- package/src/types/api/getPassphraseState.ts +4 -3
- package/src/utils/deviceInfoUtils.ts +3 -16
|
@@ -19,7 +19,7 @@ const createCommands = () => {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
describe('DeviceCommands failure mapping', () => {
|
|
22
|
-
it('does not log
|
|
22
|
+
it('does not log DeviceSessionGet response secrets', async () => {
|
|
23
23
|
const commands = createCommands();
|
|
24
24
|
const log = getLogger(LoggerNames.DeviceCommands);
|
|
25
25
|
log.messages.length = 0;
|
|
@@ -33,7 +33,7 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
33
33
|
btc_test_address: 'secret-wallet-address',
|
|
34
34
|
},
|
|
35
35
|
} as any,
|
|
36
|
-
'
|
|
36
|
+
'DeviceSessionGet'
|
|
37
37
|
)
|
|
38
38
|
).resolves.toMatchObject({ type: 'DeviceSession' });
|
|
39
39
|
|
|
@@ -41,7 +41,7 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
41
41
|
expect(JSON.stringify(log.messages)).not.toContain('secret-wallet-address');
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
it('maps an invalid
|
|
44
|
+
it('maps an invalid DeviceSessionGet resume to WalletSessionInvalid', async () => {
|
|
45
45
|
const commands = createCommands();
|
|
46
46
|
|
|
47
47
|
await expect(
|
|
@@ -54,7 +54,7 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
54
54
|
message: 'Invalid session',
|
|
55
55
|
},
|
|
56
56
|
} as any,
|
|
57
|
-
'
|
|
57
|
+
'DeviceSessionGet'
|
|
58
58
|
)
|
|
59
59
|
).rejects.toMatchObject({
|
|
60
60
|
errorCode: HardwareErrorCode.WalletSessionInvalid,
|
|
@@ -66,7 +66,7 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
66
66
|
});
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
it('maps
|
|
69
|
+
it('maps wallet-session user cancellation to ActionCancelled', async () => {
|
|
70
70
|
const commands = createCommands();
|
|
71
71
|
|
|
72
72
|
await expect(
|
|
@@ -79,7 +79,7 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
79
79
|
message: 'Cancelled on device',
|
|
80
80
|
},
|
|
81
81
|
} as any,
|
|
82
|
-
'
|
|
82
|
+
'DeviceSessionAskPassphrase'
|
|
83
83
|
)
|
|
84
84
|
).rejects.toMatchObject({
|
|
85
85
|
errorCode: HardwareErrorCode.ActionCancelled,
|
|
@@ -89,7 +89,7 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
89
89
|
it.each([
|
|
90
90
|
[3, HardwareErrorCode.DeviceCheckUnlockTypeError],
|
|
91
91
|
[4, HardwareErrorCode.DeviceNotOpenedPassphrase],
|
|
92
|
-
])('maps
|
|
92
|
+
])('maps wallet-session subcode %s to its canonical wallet error', async (subcode, code) => {
|
|
93
93
|
const commands = createCommands();
|
|
94
94
|
|
|
95
95
|
await expect(
|
|
@@ -98,7 +98,7 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
98
98
|
type: 'Failure',
|
|
99
99
|
message: { code: 'Failure_ProcessError', subcode, message: 'Session selection failed' },
|
|
100
100
|
} as any,
|
|
101
|
-
'
|
|
101
|
+
'DeviceSessionAskPin'
|
|
102
102
|
)
|
|
103
103
|
).rejects.toMatchObject({ errorCode: code });
|
|
104
104
|
});
|
|
@@ -37,7 +37,6 @@ function createDevice({ protocol }: { protocol: 'V1' | 'V2' }) {
|
|
|
37
37
|
|
|
38
38
|
describe('DeviceSettings protocol routing', () => {
|
|
39
39
|
it.each([
|
|
40
|
-
['label', { label: 'Shared Label' }],
|
|
41
40
|
['language', { language: 'ja-JP' }],
|
|
42
41
|
['brightness', { brightness: 80 }],
|
|
43
42
|
['haptic feedback', { hapticFeedback: true }],
|
|
@@ -56,6 +55,7 @@ describe('DeviceSettings protocol routing', () => {
|
|
|
56
55
|
});
|
|
57
56
|
|
|
58
57
|
it.each([
|
|
58
|
+
['label', { label: 'Shared Label' }],
|
|
59
59
|
['auto lock', { autoLockDelayMs: 60_000 }],
|
|
60
60
|
['auto shutdown', { autoShutdownDelayMs: 120_000 }],
|
|
61
61
|
['mixed settings', { brightness: 80, autoLockDelayMs: 60_000 }],
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import ClearSessionCache from '../src/api/ClearSessionCache';
|
|
4
|
+
import DeviceWipe from '../src/api/device/DeviceWipe';
|
|
4
5
|
import {
|
|
5
6
|
DeviceWalletSessionStore,
|
|
6
7
|
deviceWalletSessionStore,
|
|
@@ -96,6 +97,46 @@ describe('DeviceWalletSessionStore', () => {
|
|
|
96
97
|
});
|
|
97
98
|
});
|
|
98
99
|
|
|
100
|
+
describe('Device wipe lifecycle invalidation', () => {
|
|
101
|
+
beforeEach(() => {
|
|
102
|
+
deviceWalletSessionStore.clear();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('clears the old Protocol V2 identity and wallet sessions after wipe succeeds', async () => {
|
|
106
|
+
const device = Device.fromDescriptor({
|
|
107
|
+
id: 'pro2-descriptor',
|
|
108
|
+
path: 'pro2-path',
|
|
109
|
+
protocolType: 'V2',
|
|
110
|
+
} as never);
|
|
111
|
+
device.features = {
|
|
112
|
+
protocol: 'V2',
|
|
113
|
+
deviceId: 'old-device-id',
|
|
114
|
+
unlocked: true,
|
|
115
|
+
passphraseProtection: true,
|
|
116
|
+
} as never;
|
|
117
|
+
device.passphraseState = 'hidden-a';
|
|
118
|
+
deviceWalletSessionStore.set('old-device-id', 'hidden-a', 'old-session');
|
|
119
|
+
deviceWalletSessionStore.set('pro2-path', 'hidden-a', 'temporary-session');
|
|
120
|
+
device.markPreInitialized({ passphraseState: 'hidden-a' });
|
|
121
|
+
device.commands = {
|
|
122
|
+
typedCall: jest.fn().mockResolvedValue({ message: { message: 'accepted' } }),
|
|
123
|
+
} as never;
|
|
124
|
+
|
|
125
|
+
const method = new DeviceWipe({ id: 1, payload: { method: 'deviceWipe' } });
|
|
126
|
+
method.init();
|
|
127
|
+
(method as any).device = device;
|
|
128
|
+
|
|
129
|
+
await expect(method.run()).resolves.toEqual({ message: 'accepted' });
|
|
130
|
+
|
|
131
|
+
expect(device.features).toBeUndefined();
|
|
132
|
+
expect(device.passphraseState).toBeUndefined();
|
|
133
|
+
expect(device.needReloadDevice).toBe(true);
|
|
134
|
+
expect(device.isPreInitializedValid(60_000)).toBe(false);
|
|
135
|
+
expect(deviceWalletSessionStore.get('old-device-id', 'hidden-a')).toBeUndefined();
|
|
136
|
+
expect(deviceWalletSessionStore.get('pro2-path', 'hidden-a')).toBeUndefined();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
99
140
|
describe('ClearSessionCache', () => {
|
|
100
141
|
beforeEach(() => {
|
|
101
142
|
deviceWalletSessionStore.clear();
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import CheckAllFirmwareRelease from '../../src/api/CheckAllFirmwareRelease';
|
|
4
|
+
import { buildFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePlan';
|
|
5
|
+
import {
|
|
6
|
+
getBleFirmwareReleaseInfo,
|
|
7
|
+
getBootloaderReleaseInfo,
|
|
8
|
+
getFirmwareReleaseInfo,
|
|
9
|
+
} from '../../src/api/firmware/releaseHelper';
|
|
10
|
+
|
|
11
|
+
jest.mock('../../src/data/config', () => ({
|
|
12
|
+
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
13
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock('../../src/api/firmware/FirmwareUpdatePlan', () => ({
|
|
17
|
+
buildFirmwareUpdatePlan: jest.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
jest.mock('../../src/api/firmware/releaseHelper', () => ({
|
|
21
|
+
getBleFirmwareReleaseInfo: jest.fn(),
|
|
22
|
+
getBootloaderReleaseInfo: jest.fn(),
|
|
23
|
+
getFirmwareReleaseInfo: jest.fn(),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
const mockBuildFirmwareUpdatePlan = buildFirmwareUpdatePlan as jest.MockedFunction<
|
|
27
|
+
typeof buildFirmwareUpdatePlan
|
|
28
|
+
>;
|
|
29
|
+
const mockGetBleFirmwareReleaseInfo = getBleFirmwareReleaseInfo as jest.MockedFunction<
|
|
30
|
+
typeof getBleFirmwareReleaseInfo
|
|
31
|
+
>;
|
|
32
|
+
const mockGetBootloaderReleaseInfo = getBootloaderReleaseInfo as jest.MockedFunction<
|
|
33
|
+
typeof getBootloaderReleaseInfo
|
|
34
|
+
>;
|
|
35
|
+
const mockGetFirmwareReleaseInfo = getFirmwareReleaseInfo as jest.MockedFunction<
|
|
36
|
+
typeof getFirmwareReleaseInfo
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
describe('CheckAllFirmwareRelease', () => {
|
|
40
|
+
test('keeps release information available when an optional Plan cannot be built', async () => {
|
|
41
|
+
const firmware = {
|
|
42
|
+
status: 'outdated' as const,
|
|
43
|
+
release: {
|
|
44
|
+
url: 'https://firmware.onekey.so/classic/firmware.bin',
|
|
45
|
+
version: [3, 0, 0],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
const noUpdate = {
|
|
49
|
+
status: 'valid' as const,
|
|
50
|
+
release: undefined,
|
|
51
|
+
};
|
|
52
|
+
mockGetFirmwareReleaseInfo.mockReturnValue(firmware);
|
|
53
|
+
mockGetBootloaderReleaseInfo.mockReturnValue(noUpdate);
|
|
54
|
+
mockGetBleFirmwareReleaseInfo.mockReturnValue(noUpdate);
|
|
55
|
+
mockBuildFirmwareUpdatePlan.mockImplementation(() => {
|
|
56
|
+
throw new Error('FirmwarePlanInvalid');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const method = new CheckAllFirmwareRelease({
|
|
60
|
+
id: 1,
|
|
61
|
+
payload: {
|
|
62
|
+
method: 'checkAllFirmwareRelease',
|
|
63
|
+
platform: 'native',
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
method.init();
|
|
67
|
+
method.device = {
|
|
68
|
+
features: {
|
|
69
|
+
deviceType: EDeviceType.Classic1s,
|
|
70
|
+
serialNo: '',
|
|
71
|
+
firmwareType: EFirmwareType.Universal,
|
|
72
|
+
firmwareVersion: '1.0.0',
|
|
73
|
+
bootloaderVersion: '1.0.0',
|
|
74
|
+
},
|
|
75
|
+
} as typeof method.device;
|
|
76
|
+
|
|
77
|
+
await expect(method.run()).resolves.toEqual(
|
|
78
|
+
expect.objectContaining({
|
|
79
|
+
firmware,
|
|
80
|
+
bootloader: noUpdate,
|
|
81
|
+
ble: noUpdate,
|
|
82
|
+
firmwareUpdatePlan: undefined,
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -2,6 +2,7 @@ import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
|
|
|
2
2
|
|
|
3
3
|
import { buildFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePlan';
|
|
4
4
|
import {
|
|
5
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
5
6
|
assertFirmwareUpdatePreparedPlanBinding,
|
|
6
7
|
prepareFirmwareUpdatePlan,
|
|
7
8
|
validateFirmwareUpdatePreparedPlan,
|
|
@@ -76,6 +77,18 @@ describe('FirmwareUpdatePreparedPlan', () => {
|
|
|
76
77
|
expect(prepared.preparedPlanDigest).toMatch(/^[a-f0-9]{64}$/u);
|
|
77
78
|
expect(JSON.stringify(prepared)).not.toContain('https://');
|
|
78
79
|
expect(validateFirmwareUpdatePreparedPlan(prepared)).toEqual(prepared);
|
|
80
|
+
expect(
|
|
81
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
82
|
+
preparedPlan: prepared,
|
|
83
|
+
deviceIdentity: plan.deviceIdentity,
|
|
84
|
+
})
|
|
85
|
+
).toEqual(prepared);
|
|
86
|
+
expect(() =>
|
|
87
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
88
|
+
preparedPlan: prepared,
|
|
89
|
+
deviceIdentity: 'different-device-id',
|
|
90
|
+
})
|
|
91
|
+
).toThrow();
|
|
79
92
|
expect(
|
|
80
93
|
assertFirmwareUpdatePreparedPlanBinding({
|
|
81
94
|
preparedPlan: prepared,
|
|
@@ -60,4 +60,21 @@ describe('getLogBlockLabel', () => {
|
|
|
60
60
|
expect(getLogBlockLabel({ payload: { method, data: new Uint8Array(1024) } })).toBe(method);
|
|
61
61
|
}
|
|
62
62
|
);
|
|
63
|
+
|
|
64
|
+
it.each(['evmSignMessage', 'btcSignMessage', 'evmSignTransaction'])(
|
|
65
|
+
'blocks request and response payload logging for every API method including %s',
|
|
66
|
+
method => {
|
|
67
|
+
expect(getLogBlockLabel({ method, message: 'sensitive signing payload' })).toBe(method);
|
|
68
|
+
expect(
|
|
69
|
+
getLogBlockLabel({
|
|
70
|
+
event: 'iframe-call',
|
|
71
|
+
payload: { method, message: 'sensitive signing payload' },
|
|
72
|
+
})
|
|
73
|
+
).toBe(method);
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
it('does not classify events without an API method as method calls', () => {
|
|
78
|
+
expect(getLogBlockLabel({ event: 'DEVICE_EVENT', type: 'device-connect' })).toBeUndefined();
|
|
79
|
+
});
|
|
63
80
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EDeviceType, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
|
|
2
3
|
|
|
3
4
|
import GetPassphraseState from '../src/api/GetPassphraseState';
|
|
4
5
|
import OpenWalletSession from '../src/api/OpenWalletSession';
|
|
@@ -59,15 +60,42 @@ describe('openWalletSession', () => {
|
|
|
59
60
|
deviceWalletSessionStore.clear();
|
|
60
61
|
});
|
|
61
62
|
|
|
62
|
-
test('keeps getPassphraseState
|
|
63
|
+
test('keeps the legacy getPassphraseState App flow working on Protocol V2', async () => {
|
|
64
|
+
const typedCall = jest
|
|
65
|
+
.fn()
|
|
66
|
+
.mockResolvedValueOnce({ message: { eventless_wallet_session: true } })
|
|
67
|
+
.mockResolvedValueOnce({ message: { message: 'passphrase prepared' } })
|
|
68
|
+
.mockResolvedValueOnce({
|
|
69
|
+
message: {
|
|
70
|
+
btc_test_address: 'pro2-wallet-state',
|
|
71
|
+
session_id: 'pro2-wallet-session',
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'legacy app secret' });
|
|
63
75
|
const method = new GetPassphraseState({
|
|
64
|
-
payload: {
|
|
76
|
+
payload: {
|
|
77
|
+
method: 'getPassphraseState',
|
|
78
|
+
connectId: 'connect-id',
|
|
79
|
+
initSession: true,
|
|
80
|
+
useEmptyPassphrase: false,
|
|
81
|
+
},
|
|
65
82
|
});
|
|
66
|
-
method.device = createDevice() as any;
|
|
83
|
+
method.device = createDevice({ typedCall, promptPassphrase }) as any;
|
|
67
84
|
|
|
68
|
-
await expect(method.run()).
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
await expect(method.run()).resolves.toBe('pro2-wallet-state');
|
|
86
|
+
expect(promptPassphrase).toHaveBeenCalledWith(
|
|
87
|
+
expect.objectContaining({ deviceOnly: true }),
|
|
88
|
+
expect.anything()
|
|
89
|
+
);
|
|
90
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
91
|
+
2,
|
|
92
|
+
'DeviceSessionAskPassphrase',
|
|
93
|
+
'Success',
|
|
94
|
+
{},
|
|
95
|
+
{ timeoutMs: 120_000 }
|
|
96
|
+
);
|
|
97
|
+
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
|
|
98
|
+
expect(JSON.stringify(typedCall.mock.calls)).not.toContain('legacy app secret');
|
|
71
99
|
});
|
|
72
100
|
|
|
73
101
|
test('keeps Legacy Protocol V1 getPassphraseState parameterless', async () => {
|
|
@@ -175,12 +203,26 @@ describe('openWalletSession', () => {
|
|
|
175
203
|
walletType: 'hidden',
|
|
176
204
|
passphraseState: 'hidden-state',
|
|
177
205
|
});
|
|
206
|
+
expect(promptPassphrase).toHaveBeenCalledWith(
|
|
207
|
+
expect.objectContaining({ deviceOnly: true }),
|
|
208
|
+
expect.anything()
|
|
209
|
+
);
|
|
210
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
211
|
+
2,
|
|
212
|
+
'DeviceSessionAskPassphrase',
|
|
213
|
+
'Success',
|
|
214
|
+
{},
|
|
215
|
+
{ timeoutMs: 120_000 }
|
|
216
|
+
);
|
|
217
|
+
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
|
|
218
|
+
expect(JSON.stringify(typedCall.mock.calls)).not.toContain('hidden secret');
|
|
178
219
|
});
|
|
179
220
|
|
|
180
221
|
test('classifies the selected Pro2 wallet from the post-unlock device state', async () => {
|
|
181
222
|
const typedCall = jest
|
|
182
223
|
.fn()
|
|
183
224
|
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
225
|
+
.mockResolvedValueOnce({ message: { message: 'passphrase prepared' } })
|
|
184
226
|
.mockResolvedValueOnce({
|
|
185
227
|
message: {
|
|
186
228
|
btc_test_address: 'hidden-state-after-unlock',
|
|
@@ -210,7 +252,7 @@ describe('openWalletSession', () => {
|
|
|
210
252
|
sessionId: 'hidden-session-after-unlock',
|
|
211
253
|
resumed: false,
|
|
212
254
|
});
|
|
213
|
-
expect(device.unlockDevice).
|
|
255
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main);
|
|
214
256
|
expect(promptPassphrase).toHaveBeenCalledTimes(1);
|
|
215
257
|
});
|
|
216
258
|
|
|
@@ -246,9 +288,15 @@ describe('openWalletSession', () => {
|
|
|
246
288
|
expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
247
289
|
eventless_wallet_session: true,
|
|
248
290
|
});
|
|
249
|
-
expect(typedCall).toHaveBeenCalledWith(
|
|
250
|
-
|
|
251
|
-
|
|
291
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
292
|
+
'DeviceSessionAskPassphrase',
|
|
293
|
+
'Success',
|
|
294
|
+
{},
|
|
295
|
+
{
|
|
296
|
+
timeoutMs: 120_000,
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
252
300
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
253
301
|
expect(deviceWalletSessionStore.get('device-1', 'stale-hidden-state')).toBeUndefined();
|
|
254
302
|
expect(deviceWalletSessionStore.get('device-1', 'new-hidden-state')).toBe('new-hidden-session');
|
|
@@ -314,8 +362,8 @@ describe('openWalletSession', () => {
|
|
|
314
362
|
sessionId: 'renewed-session',
|
|
315
363
|
resumed: true,
|
|
316
364
|
});
|
|
317
|
-
expect(typedCall).toHaveBeenCalledWith('
|
|
318
|
-
|
|
365
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
366
|
+
session_id: 'known-session',
|
|
319
367
|
});
|
|
320
368
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
321
369
|
});
|
|
@@ -530,15 +578,21 @@ describe('openWalletSession', () => {
|
|
|
530
578
|
sessionId: 'hidden-session',
|
|
531
579
|
resumed: false,
|
|
532
580
|
});
|
|
533
|
-
expect(typedCall).toHaveBeenCalledWith(
|
|
534
|
-
|
|
535
|
-
|
|
581
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
582
|
+
'DeviceSessionAskPassphrase',
|
|
583
|
+
'Success',
|
|
584
|
+
{},
|
|
585
|
+
{
|
|
586
|
+
timeoutMs: 120_000,
|
|
587
|
+
}
|
|
588
|
+
);
|
|
589
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
536
590
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
537
591
|
expect(device.passphraseState).toBeUndefined();
|
|
538
592
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('hidden-session');
|
|
539
593
|
});
|
|
540
594
|
|
|
541
|
-
test('selects on-device passphrase entry
|
|
595
|
+
test('selects on-device passphrase entry before getting the prepared session', async () => {
|
|
542
596
|
const typedCall = jest.fn().mockResolvedValue({
|
|
543
597
|
message: { btc_test_address: 'device-state', session_id: 'device-session' },
|
|
544
598
|
});
|
|
@@ -553,9 +607,15 @@ describe('openWalletSession', () => {
|
|
|
553
607
|
walletType: 'hidden',
|
|
554
608
|
passphraseState: 'device-state',
|
|
555
609
|
});
|
|
556
|
-
expect(typedCall).toHaveBeenCalledWith(
|
|
557
|
-
|
|
558
|
-
|
|
610
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
611
|
+
'DeviceSessionAskPassphrase',
|
|
612
|
+
'Success',
|
|
613
|
+
{},
|
|
614
|
+
{
|
|
615
|
+
timeoutMs: 120_000,
|
|
616
|
+
}
|
|
617
|
+
);
|
|
618
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
559
619
|
});
|
|
560
620
|
|
|
561
621
|
test('selects Attach PIN only when the device reports an existing binding', async () => {
|
|
@@ -576,12 +636,11 @@ describe('openWalletSession', () => {
|
|
|
576
636
|
passphraseState: 'attach-state',
|
|
577
637
|
});
|
|
578
638
|
expect(promptPassphrase).toHaveBeenCalledWith(
|
|
579
|
-
expect.objectContaining({ existsAttachPinUser: true }),
|
|
639
|
+
expect.objectContaining({ existsAttachPinUser: true, deviceOnly: true }),
|
|
580
640
|
{ cancelDeviceOnReject: false }
|
|
581
641
|
);
|
|
582
|
-
expect(
|
|
583
|
-
|
|
584
|
-
});
|
|
642
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.AttachToPin);
|
|
643
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
585
644
|
});
|
|
586
645
|
|
|
587
646
|
test.each([
|
|
@@ -607,7 +666,7 @@ describe('openWalletSession', () => {
|
|
|
607
666
|
|
|
608
667
|
await expect(method.run()).rejects.toMatchObject({
|
|
609
668
|
errorCode: HardwareErrorCode.RuntimeError,
|
|
610
|
-
message: '
|
|
669
|
+
message: 'DeviceSessionGet returned an incomplete DeviceSession response.',
|
|
611
670
|
});
|
|
612
671
|
expect(device.clearInternalState).toHaveBeenCalled();
|
|
613
672
|
expect(device.updateInternalState).not.toHaveBeenCalled();
|
|
@@ -643,8 +702,8 @@ describe('openWalletSession', () => {
|
|
|
643
702
|
sessionId: 'renewed-session',
|
|
644
703
|
resumed: true,
|
|
645
704
|
});
|
|
646
|
-
expect(typedCall).toHaveBeenCalledWith('
|
|
647
|
-
|
|
705
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
706
|
+
session_id: 'known-session',
|
|
648
707
|
});
|
|
649
708
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
650
709
|
});
|
|
@@ -694,14 +753,14 @@ describe('openWalletSession', () => {
|
|
|
694
753
|
});
|
|
695
754
|
expect(device.unlockDevice).toHaveBeenCalledTimes(1);
|
|
696
755
|
expect(device.getDeviceState).toHaveBeenCalledTimes(2);
|
|
697
|
-
expect(typedCall).toHaveBeenCalledWith('
|
|
698
|
-
|
|
756
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
757
|
+
session_id: 'known-session',
|
|
699
758
|
});
|
|
700
|
-
const
|
|
701
|
-
([requestName]) => requestName === '
|
|
759
|
+
const sessionGetCall = typedCall.mock.calls.findIndex(
|
|
760
|
+
([requestName]) => requestName === 'DeviceSessionGet'
|
|
702
761
|
);
|
|
703
762
|
expect(device.getDeviceState.mock.invocationCallOrder[1]).toBeLessThan(
|
|
704
|
-
typedCall.mock.invocationCallOrder[
|
|
763
|
+
typedCall.mock.invocationCallOrder[sessionGetCall]
|
|
705
764
|
);
|
|
706
765
|
});
|
|
707
766
|
|