@onekeyfe/hd-core 1.2.2-alpha.1 → 1.2.2-alpha.10
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 +2 -2
- package/__tests__/device-lifecycle-events.test.ts +67 -0
- package/__tests__/open-wallet-session.test.ts +613 -80
- package/__tests__/pro2HostAssetPackage.test.ts +108 -1
- package/__tests__/protocol-v2.test.ts +194 -49
- package/__tests__/protocolV2FileWrite.test.ts +40 -0
- package/dist/api/FirmwareUpdateV4.d.ts +1 -0
- package/dist/api/FirmwareUpdateV4.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/helpers/protocolV2FileWrite.d.ts +1 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts +2 -0
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1411 -1117
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/dist/utils/pro2HostAssetPackage.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +42 -10
- package/src/api/OpenWalletSession.ts +0 -3
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +3 -1
- package/src/api/helpers/protocolV2FileWrite.ts +11 -5
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +8 -2
- package/src/core/RequestQueue.ts +16 -1
- package/src/core/index.ts +48 -21
- package/src/data/messages/messages-protocol-v2.json +1471 -1367
- package/src/protocols/protocol-v2/walletSession.ts +157 -38
- package/src/utils/pro2HostAssetPackage.ts +73 -27
|
@@ -11,6 +11,13 @@ import {
|
|
|
11
11
|
getProtocolV2WalletSession,
|
|
12
12
|
} from '../src/protocols/protocol-v2/walletSession';
|
|
13
13
|
|
|
14
|
+
const STANDARD_SEED_DOMAINS = [DeviceSessionSeedDomain.SeedDomain_Standard];
|
|
15
|
+
const CARDANO_SEED_DOMAINS = [
|
|
16
|
+
DeviceSessionSeedDomain.SeedDomain_Standard,
|
|
17
|
+
DeviceSessionSeedDomain.SeedDomain_Cardano,
|
|
18
|
+
];
|
|
19
|
+
const standardSessionGet = {};
|
|
20
|
+
|
|
14
21
|
jest.mock('../src/data/config', () => ({
|
|
15
22
|
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
16
23
|
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
@@ -19,12 +26,16 @@ jest.mock('../src/data/config', () => ({
|
|
|
19
26
|
const createDevice = ({
|
|
20
27
|
passphraseProtection = true,
|
|
21
28
|
unlockedAttachPin = false,
|
|
29
|
+
refreshedUnlocked = true,
|
|
30
|
+
refreshedPassphraseProtection = passphraseProtection,
|
|
22
31
|
refreshedUnlockedAttachPin = unlockedAttachPin,
|
|
23
32
|
typedCall = jest.fn(),
|
|
24
33
|
promptPassphrase = jest.fn(),
|
|
25
34
|
}: {
|
|
26
35
|
passphraseProtection?: boolean;
|
|
27
36
|
unlockedAttachPin?: boolean;
|
|
37
|
+
refreshedUnlocked?: boolean;
|
|
38
|
+
refreshedPassphraseProtection?: boolean;
|
|
28
39
|
refreshedUnlockedAttachPin?: boolean;
|
|
29
40
|
typedCall?: jest.Mock;
|
|
30
41
|
promptPassphrase?: jest.Mock;
|
|
@@ -43,11 +54,11 @@ const createDevice = ({
|
|
|
43
54
|
return {
|
|
44
55
|
message: {
|
|
45
56
|
device_id: 'device-1',
|
|
46
|
-
unlocked:
|
|
57
|
+
unlocked: refreshedUnlocked,
|
|
47
58
|
attach_to_pin_enabled: unlockedAttachPin,
|
|
48
59
|
unlocked_attach_pin: refreshedUnlockedAttachPin,
|
|
49
60
|
unlocked_by_attach_to_pin: refreshedUnlockedAttachPin,
|
|
50
|
-
passphrase_enabled:
|
|
61
|
+
passphrase_enabled: refreshedPassphraseProtection,
|
|
51
62
|
},
|
|
52
63
|
};
|
|
53
64
|
}
|
|
@@ -88,6 +99,8 @@ const createDevice = ({
|
|
|
88
99
|
unlockDevice: jest.fn(),
|
|
89
100
|
updateProtocolV2Status: jest.fn((status: Record<string, unknown>) => {
|
|
90
101
|
device.features.unlocked = status.unlocked ?? device.features.unlocked;
|
|
102
|
+
device.features.passphraseProtection =
|
|
103
|
+
status.passphrase_enabled ?? device.features.passphraseProtection;
|
|
91
104
|
device.features.attachToPinEnabled =
|
|
92
105
|
status.attach_to_pin_enabled ?? device.features.attachToPinEnabled;
|
|
93
106
|
device.features.unlockedAttachPin =
|
|
@@ -142,13 +155,12 @@ describe('openWalletSession', () => {
|
|
|
142
155
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
143
156
|
passphrase: '',
|
|
144
157
|
on_device: false,
|
|
158
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
145
159
|
});
|
|
146
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
147
|
-
seed_domains: [],
|
|
148
|
-
});
|
|
160
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
149
161
|
});
|
|
150
162
|
|
|
151
|
-
test('
|
|
163
|
+
test('opens the already-unlocked standard wallet without repeating Main PIN when passphrase is disabled', async () => {
|
|
152
164
|
const typedCall = jest.fn((request: string) => {
|
|
153
165
|
if (request === 'ProtocolInfoRequest') {
|
|
154
166
|
return { message: { version: 2 } };
|
|
@@ -167,19 +179,113 @@ describe('openWalletSession', () => {
|
|
|
167
179
|
|
|
168
180
|
await getProtocolV2WalletSession(device as any, { onlyMainPin: true });
|
|
169
181
|
|
|
170
|
-
expect(device.unlockDevice).
|
|
171
|
-
source: 'wallet-session-coordinator',
|
|
172
|
-
reason: 'open-wallet',
|
|
173
|
-
deviceOnly: true,
|
|
174
|
-
});
|
|
182
|
+
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
175
183
|
expect(typedCall).not.toHaveBeenCalledWith(
|
|
176
184
|
'DeviceSessionAskPassphrase',
|
|
177
185
|
'Success',
|
|
178
186
|
expect.anything()
|
|
179
187
|
);
|
|
180
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
181
|
-
|
|
188
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('refreshes stale wallet status before accepting an only-Main-PIN session', async () => {
|
|
192
|
+
let attachPinSelected = true;
|
|
193
|
+
const typedCall = jest.fn((request: string) => {
|
|
194
|
+
if (request === 'ProtocolInfoRequest') {
|
|
195
|
+
return { message: { version: 2 } };
|
|
196
|
+
}
|
|
197
|
+
if (request === 'DeviceSessionGet') {
|
|
198
|
+
return {
|
|
199
|
+
message: {
|
|
200
|
+
btc_test_address: 'standard-state',
|
|
201
|
+
session_id: 'standard-session',
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
206
|
+
return { message: {} };
|
|
207
|
+
}
|
|
208
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
209
|
+
});
|
|
210
|
+
const device = createDevice({
|
|
211
|
+
passphraseProtection: false,
|
|
212
|
+
unlockedAttachPin: false,
|
|
213
|
+
typedCall,
|
|
214
|
+
});
|
|
215
|
+
device.commands.typedCall.mockImplementation((request: string, ...args: unknown[]) => {
|
|
216
|
+
if (request === 'DeviceStatusGet') {
|
|
217
|
+
return {
|
|
218
|
+
message: {
|
|
219
|
+
device_id: 'device-1',
|
|
220
|
+
unlocked: true,
|
|
221
|
+
attach_to_pin_enabled: true,
|
|
222
|
+
unlocked_attach_pin: attachPinSelected,
|
|
223
|
+
unlocked_by_attach_to_pin: attachPinSelected,
|
|
224
|
+
passphrase_enabled: true,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return typedCall(request, ...args);
|
|
229
|
+
});
|
|
230
|
+
device.unlockDevice.mockImplementation(() => {
|
|
231
|
+
attachPinSelected = false;
|
|
232
|
+
device.features.unlockedAttachPin = false;
|
|
233
|
+
return Promise.resolve(device.features);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
await expect(
|
|
237
|
+
getProtocolV2WalletSession(device as any, { onlyMainPin: true })
|
|
238
|
+
).resolves.toMatchObject({
|
|
239
|
+
unlockedAttachPin: false,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
expect(device.commands.typedCall).toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
|
|
243
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
|
|
244
|
+
source: 'wallet-session-coordinator',
|
|
245
|
+
reason: 'open-wallet',
|
|
246
|
+
deviceOnly: true,
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test('still requires Main PIN when a cached standard session resolves to another wallet', async () => {
|
|
251
|
+
let sessionGetCount = 0;
|
|
252
|
+
const typedCall = jest.fn((request: string) => {
|
|
253
|
+
if (request === 'ProtocolInfoRequest') {
|
|
254
|
+
return { message: { version: 2 } };
|
|
255
|
+
}
|
|
256
|
+
if (request === 'DeviceSessionGet') {
|
|
257
|
+
sessionGetCount += 1;
|
|
258
|
+
return {
|
|
259
|
+
message: {
|
|
260
|
+
btc_test_address:
|
|
261
|
+
sessionGetCount === 1 ? 'unexpected-wallet-state' : 'cached-standard-state',
|
|
262
|
+
session_id:
|
|
263
|
+
sessionGetCount === 1 ? 'unexpected-wallet-session' : 'cached-standard-session',
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
268
|
+
});
|
|
269
|
+
const device = createDevice({ passphraseProtection: false, typedCall });
|
|
270
|
+
device.getStandardInternalState = jest.fn(() => ({
|
|
271
|
+
passphraseState: 'cached-standard-state',
|
|
272
|
+
sessionId: 'cached-standard-session',
|
|
273
|
+
}));
|
|
274
|
+
device.clearStandardInternalState = jest.fn();
|
|
275
|
+
|
|
276
|
+
await expect(
|
|
277
|
+
getProtocolV2WalletSession(device as any, { onlyMainPin: true })
|
|
278
|
+
).resolves.toMatchObject({
|
|
279
|
+
passphraseState: 'cached-standard-state',
|
|
280
|
+
newSession: 'cached-standard-session',
|
|
182
281
|
});
|
|
282
|
+
|
|
283
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
|
|
284
|
+
source: 'wallet-session-coordinator',
|
|
285
|
+
reason: 'session-recovery',
|
|
286
|
+
deviceOnly: true,
|
|
287
|
+
});
|
|
288
|
+
expect(sessionGetCount).toBe(2);
|
|
183
289
|
});
|
|
184
290
|
|
|
185
291
|
test('reuses a Main PIN selected by the current preflight when passphrase is disabled', async () => {
|
|
@@ -205,9 +311,7 @@ describe('openWalletSession', () => {
|
|
|
205
311
|
});
|
|
206
312
|
|
|
207
313
|
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
208
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
209
|
-
seed_domains: [],
|
|
210
|
-
});
|
|
314
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
211
315
|
});
|
|
212
316
|
|
|
213
317
|
test('does not treat an Attach PIN DeviceStatus as the Main wallet', async () => {
|
|
@@ -228,8 +332,7 @@ describe('openWalletSession', () => {
|
|
|
228
332
|
}
|
|
229
333
|
throw new Error(`Unexpected request: ${request}`);
|
|
230
334
|
});
|
|
231
|
-
const device = createDevice({ typedCall });
|
|
232
|
-
device.features.unlockedAttachPin = true;
|
|
335
|
+
const device = createDevice({ refreshedUnlockedAttachPin: true, typedCall });
|
|
233
336
|
|
|
234
337
|
await getProtocolV2WalletSession(device as any, { onlyMainPin: true });
|
|
235
338
|
|
|
@@ -334,10 +437,14 @@ describe('openWalletSession', () => {
|
|
|
334
437
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
335
438
|
passphrase: 'host hidden wallet',
|
|
336
439
|
on_device: false,
|
|
440
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
337
441
|
});
|
|
338
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
339
|
-
|
|
340
|
-
|
|
442
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
443
|
+
3,
|
|
444
|
+
'DeviceSessionGet',
|
|
445
|
+
'DeviceSession',
|
|
446
|
+
standardSessionGet
|
|
447
|
+
);
|
|
341
448
|
});
|
|
342
449
|
|
|
343
450
|
test('keeps Legacy Protocol V1 getPassphraseState parameterless', async () => {
|
|
@@ -454,10 +561,14 @@ describe('openWalletSession', () => {
|
|
|
454
561
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
455
562
|
passphrase: 'host hidden wallet',
|
|
456
563
|
on_device: false,
|
|
564
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
457
565
|
});
|
|
458
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
459
|
-
|
|
460
|
-
|
|
566
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
567
|
+
3,
|
|
568
|
+
'DeviceSessionGet',
|
|
569
|
+
'DeviceSession',
|
|
570
|
+
standardSessionGet
|
|
571
|
+
);
|
|
461
572
|
});
|
|
462
573
|
|
|
463
574
|
test('refreshes Pro2 status and unlocks before selecting a hidden wallet when locked', async () => {
|
|
@@ -731,9 +842,11 @@ describe('openWalletSession', () => {
|
|
|
731
842
|
});
|
|
732
843
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
733
844
|
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
734
|
-
expect(device.commands.typedCall).toHaveBeenCalledWith(
|
|
735
|
-
|
|
736
|
-
|
|
845
|
+
expect(device.commands.typedCall).toHaveBeenCalledWith(
|
|
846
|
+
'DeviceSessionGet',
|
|
847
|
+
'DeviceSession',
|
|
848
|
+
standardSessionGet
|
|
849
|
+
);
|
|
737
850
|
expect(device.commands.typedCall).not.toHaveBeenCalledWith(
|
|
738
851
|
'DeviceSessionAskPassphrase',
|
|
739
852
|
'Success',
|
|
@@ -829,10 +942,9 @@ describe('openWalletSession', () => {
|
|
|
829
942
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
830
943
|
passphrase: 'host hidden wallet',
|
|
831
944
|
on_device: false,
|
|
945
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
832
946
|
});
|
|
833
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
834
|
-
seed_domains: [],
|
|
835
|
-
});
|
|
947
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
836
948
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
837
949
|
expect(deviceWalletSessionStore.get('device-1', 'new-hidden-state')).toBe('new-hidden-session');
|
|
838
950
|
});
|
|
@@ -904,7 +1016,7 @@ describe('openWalletSession', () => {
|
|
|
904
1016
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
905
1017
|
session_id: 'known-session',
|
|
906
1018
|
btc_test_address: 'hidden-state',
|
|
907
|
-
|
|
1019
|
+
...standardSessionGet,
|
|
908
1020
|
});
|
|
909
1021
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
910
1022
|
});
|
|
@@ -1087,10 +1199,9 @@ describe('openWalletSession', () => {
|
|
|
1087
1199
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
1088
1200
|
passphrase: '',
|
|
1089
1201
|
on_device: false,
|
|
1202
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1090
1203
|
});
|
|
1091
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
1092
|
-
seed_domains: [],
|
|
1093
|
-
});
|
|
1204
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
1094
1205
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1095
1206
|
expect(device.passphraseState).toBeUndefined();
|
|
1096
1207
|
});
|
|
@@ -1110,7 +1221,7 @@ describe('openWalletSession', () => {
|
|
|
1110
1221
|
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'standard' },
|
|
1111
1222
|
});
|
|
1112
1223
|
method.init();
|
|
1113
|
-
const device = createDevice({ typedCall });
|
|
1224
|
+
const device = createDevice({ refreshedUnlocked: false, typedCall });
|
|
1114
1225
|
device.features.unlocked = false;
|
|
1115
1226
|
device.getDeviceState = jest
|
|
1116
1227
|
.fn()
|
|
@@ -1210,6 +1321,7 @@ describe('openWalletSession', () => {
|
|
|
1210
1321
|
});
|
|
1211
1322
|
|
|
1212
1323
|
test('switches from Attach PIN to Main PIN before opening the standard wallet', async () => {
|
|
1324
|
+
let attachPinSelected = true;
|
|
1213
1325
|
const typedCall = jest
|
|
1214
1326
|
.fn()
|
|
1215
1327
|
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
@@ -1226,6 +1338,21 @@ describe('openWalletSession', () => {
|
|
|
1226
1338
|
method.init();
|
|
1227
1339
|
const device = createDevice({ typedCall });
|
|
1228
1340
|
device.features.unlockedAttachPin = true;
|
|
1341
|
+
device.commands.typedCall.mockImplementation((request: string, ...args: unknown[]) => {
|
|
1342
|
+
if (request === 'DeviceStatusGet') {
|
|
1343
|
+
return {
|
|
1344
|
+
message: {
|
|
1345
|
+
device_id: 'device-1',
|
|
1346
|
+
unlocked: true,
|
|
1347
|
+
attach_to_pin_enabled: true,
|
|
1348
|
+
unlocked_attach_pin: attachPinSelected,
|
|
1349
|
+
unlocked_by_attach_to_pin: attachPinSelected,
|
|
1350
|
+
passphrase_enabled: true,
|
|
1351
|
+
},
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
return typedCall(request, ...args);
|
|
1355
|
+
});
|
|
1229
1356
|
device.getDeviceState = jest
|
|
1230
1357
|
.fn()
|
|
1231
1358
|
.mockResolvedValueOnce({
|
|
@@ -1245,6 +1372,7 @@ describe('openWalletSession', () => {
|
|
|
1245
1372
|
},
|
|
1246
1373
|
});
|
|
1247
1374
|
device.unlockDevice = jest.fn().mockImplementation(() => {
|
|
1375
|
+
attachPinSelected = false;
|
|
1248
1376
|
device.features.unlockedAttachPin = false;
|
|
1249
1377
|
return Promise.resolve(device.features);
|
|
1250
1378
|
});
|
|
@@ -1259,9 +1387,7 @@ describe('openWalletSession', () => {
|
|
|
1259
1387
|
reason: 'open-wallet',
|
|
1260
1388
|
deviceOnly: true,
|
|
1261
1389
|
});
|
|
1262
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
1263
|
-
seed_domains: [],
|
|
1264
|
-
});
|
|
1390
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
1265
1391
|
});
|
|
1266
1392
|
|
|
1267
1393
|
test('selects a hidden wallet without exposing the internal device session', async () => {
|
|
@@ -1294,10 +1420,9 @@ describe('openWalletSession', () => {
|
|
|
1294
1420
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
1295
1421
|
passphrase: 'host hidden wallet',
|
|
1296
1422
|
on_device: false,
|
|
1423
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1297
1424
|
});
|
|
1298
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
1299
|
-
seed_domains: [],
|
|
1300
|
-
});
|
|
1425
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
1301
1426
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
1302
1427
|
expect(device.passphraseState).toBeUndefined();
|
|
1303
1428
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('hidden-session');
|
|
@@ -1356,10 +1481,9 @@ describe('openWalletSession', () => {
|
|
|
1356
1481
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
1357
1482
|
passphrase: 'host hidden wallet',
|
|
1358
1483
|
on_device: false,
|
|
1484
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1359
1485
|
});
|
|
1360
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
1361
|
-
seed_domains: [],
|
|
1362
|
-
});
|
|
1486
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
1363
1487
|
});
|
|
1364
1488
|
|
|
1365
1489
|
test('selects an on-device passphrase wallet with an explicit on_device request', async () => {
|
|
@@ -1404,10 +1528,14 @@ describe('openWalletSession', () => {
|
|
|
1404
1528
|
});
|
|
1405
1529
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
1406
1530
|
on_device: true,
|
|
1531
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1407
1532
|
});
|
|
1408
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1409
|
-
|
|
1410
|
-
|
|
1533
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1534
|
+
3,
|
|
1535
|
+
'DeviceSessionGet',
|
|
1536
|
+
'DeviceSession',
|
|
1537
|
+
standardSessionGet
|
|
1538
|
+
);
|
|
1411
1539
|
expect(device.createProtocolV2UiPhaseMetadata).toHaveBeenNthCalledWith(
|
|
1412
1540
|
2,
|
|
1413
1541
|
'passphrase-on-device',
|
|
@@ -1458,10 +1586,14 @@ describe('openWalletSession', () => {
|
|
|
1458
1586
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
1459
1587
|
passphrase: 'host hidden wallet',
|
|
1460
1588
|
on_device: false,
|
|
1589
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1461
1590
|
});
|
|
1462
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1463
|
-
|
|
1464
|
-
|
|
1591
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1592
|
+
3,
|
|
1593
|
+
'DeviceSessionGet',
|
|
1594
|
+
'DeviceSession',
|
|
1595
|
+
standardSessionGet
|
|
1596
|
+
);
|
|
1465
1597
|
});
|
|
1466
1598
|
|
|
1467
1599
|
test('normalizes a Unicode Host passphrase before sending it to Pro2 firmware', async () => {
|
|
@@ -1483,6 +1615,7 @@ describe('openWalletSession', () => {
|
|
|
1483
1615
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
1484
1616
|
passphrase: 'cafe\u0301',
|
|
1485
1617
|
on_device: false,
|
|
1618
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1486
1619
|
});
|
|
1487
1620
|
});
|
|
1488
1621
|
|
|
@@ -1506,6 +1639,7 @@ describe('openWalletSession', () => {
|
|
|
1506
1639
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
1507
1640
|
passphrase,
|
|
1508
1641
|
on_device: false,
|
|
1642
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1509
1643
|
});
|
|
1510
1644
|
});
|
|
1511
1645
|
|
|
@@ -1541,9 +1675,7 @@ describe('openWalletSession', () => {
|
|
|
1541
1675
|
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.AttachToPin, {
|
|
1542
1676
|
emitUiEvent: false,
|
|
1543
1677
|
});
|
|
1544
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
1545
|
-
seed_domains: [],
|
|
1546
|
-
});
|
|
1678
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
1547
1679
|
});
|
|
1548
1680
|
|
|
1549
1681
|
test('uses the complete Attach PIN wire flow without a main PIN unlock', async () => {
|
|
@@ -1572,7 +1704,7 @@ describe('openWalletSession', () => {
|
|
|
1572
1704
|
['ProtocolInfoRequest', 'ProtocolInfo', { eventless_wallet_session: true }],
|
|
1573
1705
|
['DeviceSessionAskPin', 'Success', { type: DeviceSessionPinType.AttachToPin }],
|
|
1574
1706
|
['DeviceStatusGet', 'DeviceStatus', {}],
|
|
1575
|
-
['DeviceSessionGet', 'DeviceSession',
|
|
1707
|
+
['DeviceSessionGet', 'DeviceSession', standardSessionGet],
|
|
1576
1708
|
]);
|
|
1577
1709
|
expect(device.commands.typedCall).not.toHaveBeenCalledWith('DeviceSessionAskPin', 'Success', {
|
|
1578
1710
|
type: DeviceSessionPinType.Main,
|
|
@@ -1784,7 +1916,7 @@ describe('openWalletSession', () => {
|
|
|
1784
1916
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1785
1917
|
session_id: 'known-session',
|
|
1786
1918
|
btc_test_address: 'hidden-state',
|
|
1787
|
-
|
|
1919
|
+
...standardSessionGet,
|
|
1788
1920
|
});
|
|
1789
1921
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1790
1922
|
});
|
|
@@ -1844,7 +1976,7 @@ describe('openWalletSession', () => {
|
|
|
1844
1976
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1845
1977
|
session_id: 'known-session',
|
|
1846
1978
|
btc_test_address: 'hidden-state',
|
|
1847
|
-
|
|
1979
|
+
...standardSessionGet,
|
|
1848
1980
|
});
|
|
1849
1981
|
const sessionGetCall = typedCall.mock.calls.findIndex(
|
|
1850
1982
|
([requestName]) => requestName === 'DeviceSessionGet'
|
|
@@ -1942,7 +2074,7 @@ describe('openWalletSession', () => {
|
|
|
1942
2074
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1943
2075
|
session_id: 'expired-session',
|
|
1944
2076
|
btc_test_address: 'hidden-state',
|
|
1945
|
-
|
|
2077
|
+
...standardSessionGet,
|
|
1946
2078
|
});
|
|
1947
2079
|
expect(promptPassphrase).toHaveBeenCalledTimes(1);
|
|
1948
2080
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('renewed-session');
|
|
@@ -1980,46 +2112,447 @@ describe('openWalletSession', () => {
|
|
|
1980
2112
|
});
|
|
1981
2113
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1982
2114
|
btc_test_address: 'hidden-state',
|
|
1983
|
-
|
|
2115
|
+
...standardSessionGet,
|
|
1984
2116
|
});
|
|
1985
2117
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1986
2118
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('new-session');
|
|
1987
2119
|
});
|
|
1988
2120
|
|
|
1989
|
-
test.each([false, true])(
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
message: {
|
|
1996
|
-
|
|
1997
|
-
|
|
2121
|
+
test.each([false, true])(
|
|
2122
|
+
'openWalletSession does not thread CommonParams.deriveCardano: %s',
|
|
2123
|
+
async deriveCardano => {
|
|
2124
|
+
const typedCall = jest
|
|
2125
|
+
.fn()
|
|
2126
|
+
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
2127
|
+
.mockResolvedValueOnce({ message: {} })
|
|
2128
|
+
.mockResolvedValueOnce({
|
|
2129
|
+
message: {
|
|
2130
|
+
btc_test_address: 'hidden-state',
|
|
2131
|
+
session_id: 'new-session',
|
|
2132
|
+
},
|
|
2133
|
+
});
|
|
2134
|
+
const method = new OpenWalletSession({
|
|
2135
|
+
payload: {
|
|
2136
|
+
method: 'openWalletSession',
|
|
2137
|
+
connectId: 'connect-id',
|
|
2138
|
+
mode: 'select-hidden',
|
|
2139
|
+
deriveCardano,
|
|
1998
2140
|
},
|
|
1999
2141
|
});
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2142
|
+
method.init();
|
|
2143
|
+
method.device = createDevice({
|
|
2144
|
+
typedCall,
|
|
2145
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' }),
|
|
2146
|
+
}) as any;
|
|
2147
|
+
|
|
2148
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
2149
|
+
walletType: 'hidden',
|
|
2150
|
+
passphraseState: 'hidden-state',
|
|
2151
|
+
});
|
|
2152
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
2153
|
+
passphrase: 'host hidden wallet',
|
|
2154
|
+
on_device: false,
|
|
2155
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
2156
|
+
});
|
|
2157
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
2158
|
+
'DeviceSessionGet',
|
|
2159
|
+
'DeviceSession',
|
|
2160
|
+
standardSessionGet
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
);
|
|
2164
|
+
|
|
2165
|
+
test('requests Cardano seed domains when a V2 session rebuild has Cardano intent', async () => {
|
|
2166
|
+
const typedCall = jest.fn((request: string) => {
|
|
2167
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2168
|
+
return { message: { version: 2 } };
|
|
2169
|
+
}
|
|
2170
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
2171
|
+
return { message: {} };
|
|
2172
|
+
}
|
|
2173
|
+
if (request === 'DeviceSessionGet') {
|
|
2174
|
+
return {
|
|
2175
|
+
message: {
|
|
2176
|
+
btc_test_address: 'hidden-state',
|
|
2177
|
+
session_id: 'new-session',
|
|
2178
|
+
seed_domains: [
|
|
2179
|
+
DeviceSessionSeedDomain.SeedDomain_Standard,
|
|
2180
|
+
DeviceSessionSeedDomain.SeedDomain_Cardano,
|
|
2181
|
+
],
|
|
2182
|
+
},
|
|
2183
|
+
};
|
|
2184
|
+
}
|
|
2185
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2007
2186
|
});
|
|
2008
|
-
|
|
2009
|
-
method.device = createDevice({
|
|
2187
|
+
const device = createDevice({
|
|
2010
2188
|
typedCall,
|
|
2011
2189
|
promptPassphrase: jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' }),
|
|
2012
|
-
})
|
|
2190
|
+
});
|
|
2013
2191
|
|
|
2014
|
-
await expect(
|
|
2015
|
-
|
|
2192
|
+
await expect(
|
|
2193
|
+
getProtocolV2WalletSession(device as any, {
|
|
2194
|
+
forceWalletSelection: true,
|
|
2195
|
+
deriveCardano: true,
|
|
2196
|
+
})
|
|
2197
|
+
).resolves.toMatchObject({
|
|
2016
2198
|
passphraseState: 'hidden-state',
|
|
2199
|
+
newSession: 'new-session',
|
|
2017
2200
|
});
|
|
2201
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
2202
|
+
passphrase: 'host hidden wallet',
|
|
2203
|
+
on_device: false,
|
|
2204
|
+
seed_domains: CARDANO_SEED_DOMAINS,
|
|
2205
|
+
});
|
|
2206
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
2207
|
+
});
|
|
2208
|
+
|
|
2209
|
+
test('asks Cardano seed domains after Get reports a Standard-only session', async () => {
|
|
2210
|
+
const typedCall = jest.fn((request: string) => {
|
|
2211
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2212
|
+
return { message: { version: 2 } };
|
|
2213
|
+
}
|
|
2214
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
2215
|
+
return { message: {} };
|
|
2216
|
+
}
|
|
2217
|
+
if (request === 'DeviceSessionGet') {
|
|
2218
|
+
const hasCardanoAsk = typedCall.mock.calls.some(
|
|
2219
|
+
call =>
|
|
2220
|
+
call[0] === 'DeviceSessionAskPassphrase' &&
|
|
2221
|
+
Array.isArray(call[2]?.seed_domains) &&
|
|
2222
|
+
call[2].seed_domains.includes(DeviceSessionSeedDomain.SeedDomain_Cardano)
|
|
2223
|
+
);
|
|
2224
|
+
return {
|
|
2225
|
+
message: {
|
|
2226
|
+
btc_test_address: 'hidden-state',
|
|
2227
|
+
session_id: 'new-session',
|
|
2228
|
+
seed_domains: hasCardanoAsk
|
|
2229
|
+
? [
|
|
2230
|
+
DeviceSessionSeedDomain.SeedDomain_Standard,
|
|
2231
|
+
DeviceSessionSeedDomain.SeedDomain_Cardano,
|
|
2232
|
+
]
|
|
2233
|
+
: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
2234
|
+
},
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2238
|
+
});
|
|
2239
|
+
const device = createDevice({
|
|
2240
|
+
typedCall,
|
|
2241
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' }),
|
|
2242
|
+
});
|
|
2243
|
+
device.passphraseState = 'hidden-state';
|
|
2244
|
+
deviceWalletSessionStore.set('device-1', 'hidden-state', 'cached-session');
|
|
2245
|
+
|
|
2246
|
+
await getProtocolV2WalletSession(device as any, {
|
|
2247
|
+
expectedPassphraseState: 'hidden-state',
|
|
2248
|
+
deriveCardano: true,
|
|
2249
|
+
});
|
|
2250
|
+
|
|
2018
2251
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
2252
|
+
session_id: 'cached-session',
|
|
2253
|
+
btc_test_address: 'hidden-state',
|
|
2254
|
+
});
|
|
2255
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
2256
|
+
passphrase: 'host hidden wallet',
|
|
2257
|
+
on_device: false,
|
|
2019
2258
|
seed_domains: [
|
|
2020
2259
|
DeviceSessionSeedDomain.SeedDomain_Standard,
|
|
2021
|
-
|
|
2260
|
+
DeviceSessionSeedDomain.SeedDomain_Cardano,
|
|
2022
2261
|
],
|
|
2023
2262
|
});
|
|
2024
2263
|
});
|
|
2264
|
+
|
|
2265
|
+
test('uses empty AskPassphrase to add Cardano on an Attach PIN session', async () => {
|
|
2266
|
+
const typedCall = jest.fn((request: string) => {
|
|
2267
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2268
|
+
return { message: { version: 2 } };
|
|
2269
|
+
}
|
|
2270
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
2271
|
+
return { message: {} };
|
|
2272
|
+
}
|
|
2273
|
+
if (request === 'DeviceSessionGet') {
|
|
2274
|
+
const askedCardano = typedCall.mock.calls.some(
|
|
2275
|
+
call =>
|
|
2276
|
+
call[0] === 'DeviceSessionAskPassphrase' &&
|
|
2277
|
+
Array.isArray(call[2]?.seed_domains) &&
|
|
2278
|
+
call[2].seed_domains.includes(DeviceSessionSeedDomain.SeedDomain_Cardano)
|
|
2279
|
+
);
|
|
2280
|
+
return {
|
|
2281
|
+
message: {
|
|
2282
|
+
btc_test_address: 'attach-state',
|
|
2283
|
+
session_id: 'attach-session',
|
|
2284
|
+
seed_domains: askedCardano ? CARDANO_SEED_DOMAINS : STANDARD_SEED_DOMAINS,
|
|
2285
|
+
},
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2289
|
+
});
|
|
2290
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'should-not-ask' });
|
|
2291
|
+
const device = createDevice({
|
|
2292
|
+
typedCall,
|
|
2293
|
+
promptPassphrase,
|
|
2294
|
+
unlockedAttachPin: true,
|
|
2295
|
+
});
|
|
2296
|
+
|
|
2297
|
+
await expect(
|
|
2298
|
+
getProtocolV2WalletSession(device as any, {
|
|
2299
|
+
readCurrentAttachPinSession: true,
|
|
2300
|
+
deriveCardano: true,
|
|
2301
|
+
})
|
|
2302
|
+
).resolves.toMatchObject({
|
|
2303
|
+
passphraseState: 'attach-state',
|
|
2304
|
+
newSession: 'attach-session',
|
|
2305
|
+
unlockedAttachPin: true,
|
|
2306
|
+
});
|
|
2307
|
+
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
2308
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
2309
|
+
passphrase: '',
|
|
2310
|
+
on_device: false,
|
|
2311
|
+
seed_domains: CARDANO_SEED_DOMAINS,
|
|
2312
|
+
});
|
|
2313
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
2314
|
+
expect(
|
|
2315
|
+
typedCall.mock.calls
|
|
2316
|
+
.filter(call => call[0] === 'DeviceSessionGet')
|
|
2317
|
+
.every(call => !('seed_domains' in (call[2] ?? {})))
|
|
2318
|
+
).toBe(true);
|
|
2319
|
+
});
|
|
2320
|
+
|
|
2321
|
+
test('uses empty AskPassphrase after selecting Attach PIN for Cardano intent', async () => {
|
|
2322
|
+
const typedCall = jest.fn((request: string) => {
|
|
2323
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2324
|
+
return { message: { version: 2 } };
|
|
2325
|
+
}
|
|
2326
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
2327
|
+
return { message: {} };
|
|
2328
|
+
}
|
|
2329
|
+
if (request === 'DeviceSessionGet') {
|
|
2330
|
+
return {
|
|
2331
|
+
message: {
|
|
2332
|
+
btc_test_address: 'attach-state',
|
|
2333
|
+
session_id: 'attach-session',
|
|
2334
|
+
seed_domains: CARDANO_SEED_DOMAINS,
|
|
2335
|
+
},
|
|
2336
|
+
};
|
|
2337
|
+
}
|
|
2338
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2339
|
+
});
|
|
2340
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ attachPinOnDevice: true });
|
|
2341
|
+
const device = createDevice({ typedCall, promptPassphrase });
|
|
2342
|
+
device.features.attachToPinEnabled = true;
|
|
2343
|
+
|
|
2344
|
+
await expect(
|
|
2345
|
+
getProtocolV2WalletSession(device as any, {
|
|
2346
|
+
forceWalletSelection: true,
|
|
2347
|
+
deriveCardano: true,
|
|
2348
|
+
})
|
|
2349
|
+
).resolves.toMatchObject({
|
|
2350
|
+
passphraseState: 'attach-state',
|
|
2351
|
+
newSession: 'attach-session',
|
|
2352
|
+
unlockedAttachPin: true,
|
|
2353
|
+
});
|
|
2354
|
+
expect(promptPassphrase).toHaveBeenCalledTimes(1);
|
|
2355
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(
|
|
2356
|
+
DeviceSessionPinType.AttachToPin,
|
|
2357
|
+
expect.objectContaining({ emitUiEvent: false })
|
|
2358
|
+
);
|
|
2359
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
2360
|
+
passphrase: '',
|
|
2361
|
+
on_device: false,
|
|
2362
|
+
seed_domains: CARDANO_SEED_DOMAINS,
|
|
2363
|
+
});
|
|
2364
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
2365
|
+
expect(
|
|
2366
|
+
typedCall.mock.calls
|
|
2367
|
+
.filter(call => call[0] === 'DeviceSessionGet')
|
|
2368
|
+
.every(call => !('seed_domains' in (call[2] ?? {})))
|
|
2369
|
+
).toBe(true);
|
|
2370
|
+
});
|
|
2371
|
+
|
|
2372
|
+
test('still locks before a passphrase picker after Attach PIN Cardano empty Ask', async () => {
|
|
2373
|
+
const typedCall = jest.fn((request: string) => {
|
|
2374
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2375
|
+
return { message: { version: 2 } };
|
|
2376
|
+
}
|
|
2377
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
2378
|
+
return { message: {} };
|
|
2379
|
+
}
|
|
2380
|
+
if (request === 'DeviceSessionGet') {
|
|
2381
|
+
return {
|
|
2382
|
+
message: {
|
|
2383
|
+
btc_test_address: 'attach-state',
|
|
2384
|
+
session_id: 'attach-session',
|
|
2385
|
+
seed_domains: CARDANO_SEED_DOMAINS,
|
|
2386
|
+
},
|
|
2387
|
+
};
|
|
2388
|
+
}
|
|
2389
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2390
|
+
});
|
|
2391
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'should-not-ask' });
|
|
2392
|
+
const device = createDevice({
|
|
2393
|
+
typedCall,
|
|
2394
|
+
promptPassphrase,
|
|
2395
|
+
unlockedAttachPin: true,
|
|
2396
|
+
});
|
|
2397
|
+
|
|
2398
|
+
await getProtocolV2WalletSession(device as any, {
|
|
2399
|
+
readCurrentAttachPinSession: true,
|
|
2400
|
+
deriveCardano: true,
|
|
2401
|
+
});
|
|
2402
|
+
await expect(
|
|
2403
|
+
getProtocolV2WalletSession(device as any, { forceWalletSelection: true })
|
|
2404
|
+
).rejects.toMatchObject({
|
|
2405
|
+
errorCode: HardwareErrorCode.DeviceCheckUnlockTypeError,
|
|
2406
|
+
});
|
|
2407
|
+
expect(device.lockDevice).toHaveBeenCalled();
|
|
2408
|
+
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
2409
|
+
});
|
|
2410
|
+
|
|
2411
|
+
test('uses a second Get for Cardano when passphrase protection is off', async () => {
|
|
2412
|
+
const typedCall = jest.fn((request: string) => {
|
|
2413
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2414
|
+
return { message: { version: 2 } };
|
|
2415
|
+
}
|
|
2416
|
+
if (request === 'DeviceSessionGet') {
|
|
2417
|
+
const getCount = typedCall.mock.calls.filter(call => call[0] === 'DeviceSessionGet').length;
|
|
2418
|
+
return {
|
|
2419
|
+
message: {
|
|
2420
|
+
btc_test_address: 'standard-state',
|
|
2421
|
+
session_id: 'standard-session',
|
|
2422
|
+
seed_domains: getCount > 1 ? CARDANO_SEED_DOMAINS : STANDARD_SEED_DOMAINS,
|
|
2423
|
+
},
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2426
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2427
|
+
});
|
|
2428
|
+
const device = createDevice({
|
|
2429
|
+
typedCall,
|
|
2430
|
+
passphraseProtection: false,
|
|
2431
|
+
});
|
|
2432
|
+
|
|
2433
|
+
await expect(
|
|
2434
|
+
getProtocolV2WalletSession(device as any, {
|
|
2435
|
+
onlyMainPin: true,
|
|
2436
|
+
deriveCardano: true,
|
|
2437
|
+
})
|
|
2438
|
+
).resolves.toMatchObject({
|
|
2439
|
+
passphraseState: 'standard-state',
|
|
2440
|
+
newSession: 'standard-session',
|
|
2441
|
+
});
|
|
2442
|
+
expect(typedCall).not.toHaveBeenCalledWith(
|
|
2443
|
+
'DeviceSessionAskPassphrase',
|
|
2444
|
+
'Success',
|
|
2445
|
+
expect.anything()
|
|
2446
|
+
);
|
|
2447
|
+
expect(typedCall.mock.calls.filter(call => call[0] === 'DeviceSessionGet')).toHaveLength(2);
|
|
2448
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
2449
|
+
});
|
|
2450
|
+
|
|
2451
|
+
test('fails closed when Cardano fallback still lacks Cardano', async () => {
|
|
2452
|
+
const typedCall = jest.fn((request: string) => {
|
|
2453
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2454
|
+
return { message: { version: 2 } };
|
|
2455
|
+
}
|
|
2456
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
2457
|
+
return { message: {} };
|
|
2458
|
+
}
|
|
2459
|
+
if (request === 'DeviceSessionGet') {
|
|
2460
|
+
return {
|
|
2461
|
+
message: {
|
|
2462
|
+
btc_test_address: 'hidden-state',
|
|
2463
|
+
session_id: 'hidden-session',
|
|
2464
|
+
seed_domains: STANDARD_SEED_DOMAINS,
|
|
2465
|
+
},
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2469
|
+
});
|
|
2470
|
+
const device = createDevice({
|
|
2471
|
+
typedCall,
|
|
2472
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' }),
|
|
2473
|
+
});
|
|
2474
|
+
deviceWalletSessionStore.set('device-1', 'hidden-state', 'cached-session');
|
|
2475
|
+
|
|
2476
|
+
await expect(
|
|
2477
|
+
getProtocolV2WalletSession(device as any, {
|
|
2478
|
+
expectedPassphraseState: 'hidden-state',
|
|
2479
|
+
deriveCardano: true,
|
|
2480
|
+
})
|
|
2481
|
+
).rejects.toMatchObject({
|
|
2482
|
+
errorCode: HardwareErrorCode.WalletSessionInvalid,
|
|
2483
|
+
});
|
|
2484
|
+
expect(device.clearInternalState).toHaveBeenCalled();
|
|
2485
|
+
});
|
|
2486
|
+
|
|
2487
|
+
test('locks before selecting a passphrase wallet from an Attach PIN session', async () => {
|
|
2488
|
+
const typedCall = jest.fn((request: string) => {
|
|
2489
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2490
|
+
return { message: { version: 2 } };
|
|
2491
|
+
}
|
|
2492
|
+
if (request === 'DeviceStatusGet') {
|
|
2493
|
+
return {
|
|
2494
|
+
message: {
|
|
2495
|
+
device_id: 'device-1',
|
|
2496
|
+
unlocked: true,
|
|
2497
|
+
passphrase_enabled: true,
|
|
2498
|
+
unlocked_by_attach_to_pin: true,
|
|
2499
|
+
},
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2503
|
+
});
|
|
2504
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'should-not-ask' });
|
|
2505
|
+
const device = createDevice({
|
|
2506
|
+
typedCall,
|
|
2507
|
+
promptPassphrase,
|
|
2508
|
+
unlockedAttachPin: true,
|
|
2509
|
+
});
|
|
2510
|
+
|
|
2511
|
+
await expect(
|
|
2512
|
+
getProtocolV2WalletSession(device as any, { forceWalletSelection: true })
|
|
2513
|
+
).rejects.toMatchObject({
|
|
2514
|
+
errorCode: HardwareErrorCode.DeviceCheckUnlockTypeError,
|
|
2515
|
+
});
|
|
2516
|
+
expect(device.lockDevice).toHaveBeenCalled();
|
|
2517
|
+
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
2518
|
+
expect(typedCall).not.toHaveBeenCalledWith(
|
|
2519
|
+
'DeviceSessionAskPassphrase',
|
|
2520
|
+
'Success',
|
|
2521
|
+
expect.anything()
|
|
2522
|
+
);
|
|
2523
|
+
});
|
|
2524
|
+
|
|
2525
|
+
test('asks Standard-only seed domains when deriveCardano is false', async () => {
|
|
2526
|
+
const typedCall = jest.fn((request: string) => {
|
|
2527
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2528
|
+
return { message: { version: 2 } };
|
|
2529
|
+
}
|
|
2530
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
2531
|
+
return { message: {} };
|
|
2532
|
+
}
|
|
2533
|
+
if (request === 'DeviceSessionGet') {
|
|
2534
|
+
return {
|
|
2535
|
+
message: {
|
|
2536
|
+
btc_test_address: 'hidden-state',
|
|
2537
|
+
session_id: 'new-session',
|
|
2538
|
+
},
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2542
|
+
});
|
|
2543
|
+
const device = createDevice({
|
|
2544
|
+
typedCall,
|
|
2545
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' }),
|
|
2546
|
+
});
|
|
2547
|
+
|
|
2548
|
+
await getProtocolV2WalletSession(device as any, {
|
|
2549
|
+
forceWalletSelection: true,
|
|
2550
|
+
deriveCardano: false,
|
|
2551
|
+
});
|
|
2552
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
2553
|
+
passphrase: 'host hidden wallet',
|
|
2554
|
+
on_device: false,
|
|
2555
|
+
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
2556
|
+
});
|
|
2557
|
+
});
|
|
2025
2558
|
});
|