@onekeyfe/hd-core 1.2.2-alpha.5 → 1.2.2-alpha.6
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__/open-wallet-session.test.ts +318 -20
- package/__tests__/protocol-v2.test.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +110 -27
- 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/package.json +4 -4
- package/src/data/messages/messages-protocol-v2.json +39 -10
- package/src/protocols/protocol-v2/walletSession.ts +95 -15
|
@@ -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/',
|
|
@@ -144,7 +151,7 @@ describe('openWalletSession', () => {
|
|
|
144
151
|
on_device: false,
|
|
145
152
|
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
146
153
|
});
|
|
147
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
154
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
148
155
|
});
|
|
149
156
|
|
|
150
157
|
test('selects the Main PIN before opening the standard wallet when passphrase is disabled', async () => {
|
|
@@ -176,7 +183,7 @@ describe('openWalletSession', () => {
|
|
|
176
183
|
'Success',
|
|
177
184
|
expect.anything()
|
|
178
185
|
);
|
|
179
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
186
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
180
187
|
});
|
|
181
188
|
|
|
182
189
|
test('reuses a Main PIN selected by the current preflight when passphrase is disabled', async () => {
|
|
@@ -202,7 +209,7 @@ describe('openWalletSession', () => {
|
|
|
202
209
|
});
|
|
203
210
|
|
|
204
211
|
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
205
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
212
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
206
213
|
});
|
|
207
214
|
|
|
208
215
|
test('does not treat an Attach PIN DeviceStatus as the Main wallet', async () => {
|
|
@@ -331,7 +338,12 @@ describe('openWalletSession', () => {
|
|
|
331
338
|
on_device: false,
|
|
332
339
|
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
333
340
|
});
|
|
334
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
341
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
342
|
+
3,
|
|
343
|
+
'DeviceSessionGet',
|
|
344
|
+
'DeviceSession',
|
|
345
|
+
standardSessionGet
|
|
346
|
+
);
|
|
335
347
|
});
|
|
336
348
|
|
|
337
349
|
test('keeps Legacy Protocol V1 getPassphraseState parameterless', async () => {
|
|
@@ -450,7 +462,12 @@ describe('openWalletSession', () => {
|
|
|
450
462
|
on_device: false,
|
|
451
463
|
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
452
464
|
});
|
|
453
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
465
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
466
|
+
3,
|
|
467
|
+
'DeviceSessionGet',
|
|
468
|
+
'DeviceSession',
|
|
469
|
+
standardSessionGet
|
|
470
|
+
);
|
|
454
471
|
});
|
|
455
472
|
|
|
456
473
|
test('refreshes Pro2 status and unlocks before selecting a hidden wallet when locked', async () => {
|
|
@@ -724,7 +741,11 @@ describe('openWalletSession', () => {
|
|
|
724
741
|
});
|
|
725
742
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
726
743
|
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
727
|
-
expect(device.commands.typedCall).toHaveBeenCalledWith(
|
|
744
|
+
expect(device.commands.typedCall).toHaveBeenCalledWith(
|
|
745
|
+
'DeviceSessionGet',
|
|
746
|
+
'DeviceSession',
|
|
747
|
+
standardSessionGet
|
|
748
|
+
);
|
|
728
749
|
expect(device.commands.typedCall).not.toHaveBeenCalledWith(
|
|
729
750
|
'DeviceSessionAskPassphrase',
|
|
730
751
|
'Success',
|
|
@@ -822,7 +843,7 @@ describe('openWalletSession', () => {
|
|
|
822
843
|
on_device: false,
|
|
823
844
|
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
824
845
|
});
|
|
825
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
846
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
826
847
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
827
848
|
expect(deviceWalletSessionStore.get('device-1', 'new-hidden-state')).toBe('new-hidden-session');
|
|
828
849
|
});
|
|
@@ -894,6 +915,7 @@ describe('openWalletSession', () => {
|
|
|
894
915
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
895
916
|
session_id: 'known-session',
|
|
896
917
|
btc_test_address: 'hidden-state',
|
|
918
|
+
...standardSessionGet,
|
|
897
919
|
});
|
|
898
920
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
899
921
|
});
|
|
@@ -1078,7 +1100,7 @@ describe('openWalletSession', () => {
|
|
|
1078
1100
|
on_device: false,
|
|
1079
1101
|
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1080
1102
|
});
|
|
1081
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
1103
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
1082
1104
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1083
1105
|
expect(device.passphraseState).toBeUndefined();
|
|
1084
1106
|
});
|
|
@@ -1247,7 +1269,7 @@ describe('openWalletSession', () => {
|
|
|
1247
1269
|
reason: 'open-wallet',
|
|
1248
1270
|
deviceOnly: true,
|
|
1249
1271
|
});
|
|
1250
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
1272
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
1251
1273
|
});
|
|
1252
1274
|
|
|
1253
1275
|
test('selects a hidden wallet without exposing the internal device session', async () => {
|
|
@@ -1282,7 +1304,7 @@ describe('openWalletSession', () => {
|
|
|
1282
1304
|
on_device: false,
|
|
1283
1305
|
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1284
1306
|
});
|
|
1285
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
1307
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
1286
1308
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
1287
1309
|
expect(device.passphraseState).toBeUndefined();
|
|
1288
1310
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('hidden-session');
|
|
@@ -1343,7 +1365,7 @@ describe('openWalletSession', () => {
|
|
|
1343
1365
|
on_device: false,
|
|
1344
1366
|
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1345
1367
|
});
|
|
1346
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
1368
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
1347
1369
|
});
|
|
1348
1370
|
|
|
1349
1371
|
test('selects an on-device passphrase wallet with an explicit on_device request', async () => {
|
|
@@ -1390,7 +1412,12 @@ describe('openWalletSession', () => {
|
|
|
1390
1412
|
on_device: true,
|
|
1391
1413
|
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1392
1414
|
});
|
|
1393
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1415
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1416
|
+
3,
|
|
1417
|
+
'DeviceSessionGet',
|
|
1418
|
+
'DeviceSession',
|
|
1419
|
+
standardSessionGet
|
|
1420
|
+
);
|
|
1394
1421
|
expect(device.createProtocolV2UiPhaseMetadata).toHaveBeenNthCalledWith(
|
|
1395
1422
|
2,
|
|
1396
1423
|
'passphrase-on-device',
|
|
@@ -1443,7 +1470,12 @@ describe('openWalletSession', () => {
|
|
|
1443
1470
|
on_device: false,
|
|
1444
1471
|
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
1445
1472
|
});
|
|
1446
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1473
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1474
|
+
3,
|
|
1475
|
+
'DeviceSessionGet',
|
|
1476
|
+
'DeviceSession',
|
|
1477
|
+
standardSessionGet
|
|
1478
|
+
);
|
|
1447
1479
|
});
|
|
1448
1480
|
|
|
1449
1481
|
test('normalizes a Unicode Host passphrase before sending it to Pro2 firmware', async () => {
|
|
@@ -1525,7 +1557,7 @@ describe('openWalletSession', () => {
|
|
|
1525
1557
|
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.AttachToPin, {
|
|
1526
1558
|
emitUiEvent: false,
|
|
1527
1559
|
});
|
|
1528
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession',
|
|
1560
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
1529
1561
|
});
|
|
1530
1562
|
|
|
1531
1563
|
test('uses the complete Attach PIN wire flow without a main PIN unlock', async () => {
|
|
@@ -1554,7 +1586,7 @@ describe('openWalletSession', () => {
|
|
|
1554
1586
|
['ProtocolInfoRequest', 'ProtocolInfo', { eventless_wallet_session: true }],
|
|
1555
1587
|
['DeviceSessionAskPin', 'Success', { type: DeviceSessionPinType.AttachToPin }],
|
|
1556
1588
|
['DeviceStatusGet', 'DeviceStatus', {}],
|
|
1557
|
-
['DeviceSessionGet', 'DeviceSession',
|
|
1589
|
+
['DeviceSessionGet', 'DeviceSession', standardSessionGet],
|
|
1558
1590
|
]);
|
|
1559
1591
|
expect(device.commands.typedCall).not.toHaveBeenCalledWith('DeviceSessionAskPin', 'Success', {
|
|
1560
1592
|
type: DeviceSessionPinType.Main,
|
|
@@ -1766,6 +1798,7 @@ describe('openWalletSession', () => {
|
|
|
1766
1798
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1767
1799
|
session_id: 'known-session',
|
|
1768
1800
|
btc_test_address: 'hidden-state',
|
|
1801
|
+
...standardSessionGet,
|
|
1769
1802
|
});
|
|
1770
1803
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1771
1804
|
});
|
|
@@ -1825,6 +1858,7 @@ describe('openWalletSession', () => {
|
|
|
1825
1858
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1826
1859
|
session_id: 'known-session',
|
|
1827
1860
|
btc_test_address: 'hidden-state',
|
|
1861
|
+
...standardSessionGet,
|
|
1828
1862
|
});
|
|
1829
1863
|
const sessionGetCall = typedCall.mock.calls.findIndex(
|
|
1830
1864
|
([requestName]) => requestName === 'DeviceSessionGet'
|
|
@@ -1922,6 +1956,7 @@ describe('openWalletSession', () => {
|
|
|
1922
1956
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1923
1957
|
session_id: 'expired-session',
|
|
1924
1958
|
btc_test_address: 'hidden-state',
|
|
1959
|
+
...standardSessionGet,
|
|
1925
1960
|
});
|
|
1926
1961
|
expect(promptPassphrase).toHaveBeenCalledTimes(1);
|
|
1927
1962
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('renewed-session');
|
|
@@ -1959,6 +1994,7 @@ describe('openWalletSession', () => {
|
|
|
1959
1994
|
});
|
|
1960
1995
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1961
1996
|
btc_test_address: 'hidden-state',
|
|
1997
|
+
...standardSessionGet,
|
|
1962
1998
|
});
|
|
1963
1999
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1964
2000
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('new-session');
|
|
@@ -2000,7 +2036,11 @@ describe('openWalletSession', () => {
|
|
|
2000
2036
|
on_device: false,
|
|
2001
2037
|
seed_domains: [DeviceSessionSeedDomain.SeedDomain_Standard],
|
|
2002
2038
|
});
|
|
2003
|
-
expect(typedCall).toHaveBeenCalledWith(
|
|
2039
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
2040
|
+
'DeviceSessionGet',
|
|
2041
|
+
'DeviceSession',
|
|
2042
|
+
standardSessionGet
|
|
2043
|
+
);
|
|
2004
2044
|
}
|
|
2005
2045
|
);
|
|
2006
2046
|
|
|
@@ -2043,11 +2083,9 @@ describe('openWalletSession', () => {
|
|
|
2043
2083
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
2044
2084
|
passphrase: 'host hidden wallet',
|
|
2045
2085
|
on_device: false,
|
|
2046
|
-
seed_domains:
|
|
2047
|
-
DeviceSessionSeedDomain.SeedDomain_Standard,
|
|
2048
|
-
DeviceSessionSeedDomain.SeedDomain_Cardano,
|
|
2049
|
-
],
|
|
2086
|
+
seed_domains: CARDANO_SEED_DOMAINS,
|
|
2050
2087
|
});
|
|
2088
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
|
|
2051
2089
|
});
|
|
2052
2090
|
|
|
2053
2091
|
test('asks Cardano seed domains after Get reports a Standard-only session', async () => {
|
|
@@ -2106,6 +2144,266 @@ describe('openWalletSession', () => {
|
|
|
2106
2144
|
});
|
|
2107
2145
|
});
|
|
2108
2146
|
|
|
2147
|
+
test('uses empty AskPassphrase to add Cardano on an Attach PIN session', async () => {
|
|
2148
|
+
const typedCall = jest.fn((request: string) => {
|
|
2149
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2150
|
+
return { message: { version: 2 } };
|
|
2151
|
+
}
|
|
2152
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
2153
|
+
return { message: {} };
|
|
2154
|
+
}
|
|
2155
|
+
if (request === 'DeviceSessionGet') {
|
|
2156
|
+
const askedCardano = typedCall.mock.calls.some(
|
|
2157
|
+
call =>
|
|
2158
|
+
call[0] === 'DeviceSessionAskPassphrase' &&
|
|
2159
|
+
Array.isArray(call[2]?.seed_domains) &&
|
|
2160
|
+
call[2].seed_domains.includes(DeviceSessionSeedDomain.SeedDomain_Cardano)
|
|
2161
|
+
);
|
|
2162
|
+
return {
|
|
2163
|
+
message: {
|
|
2164
|
+
btc_test_address: 'attach-state',
|
|
2165
|
+
session_id: 'attach-session',
|
|
2166
|
+
seed_domains: askedCardano ? CARDANO_SEED_DOMAINS : STANDARD_SEED_DOMAINS,
|
|
2167
|
+
},
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2170
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2171
|
+
});
|
|
2172
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'should-not-ask' });
|
|
2173
|
+
const device = createDevice({
|
|
2174
|
+
typedCall,
|
|
2175
|
+
promptPassphrase,
|
|
2176
|
+
unlockedAttachPin: true,
|
|
2177
|
+
});
|
|
2178
|
+
|
|
2179
|
+
await expect(
|
|
2180
|
+
getProtocolV2WalletSession(device as any, {
|
|
2181
|
+
readCurrentAttachPinSession: true,
|
|
2182
|
+
deriveCardano: true,
|
|
2183
|
+
})
|
|
2184
|
+
).resolves.toMatchObject({
|
|
2185
|
+
passphraseState: 'attach-state',
|
|
2186
|
+
newSession: 'attach-session',
|
|
2187
|
+
unlockedAttachPin: true,
|
|
2188
|
+
});
|
|
2189
|
+
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
2190
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
2191
|
+
passphrase: '',
|
|
2192
|
+
on_device: false,
|
|
2193
|
+
seed_domains: CARDANO_SEED_DOMAINS,
|
|
2194
|
+
});
|
|
2195
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
2196
|
+
expect(
|
|
2197
|
+
typedCall.mock.calls
|
|
2198
|
+
.filter(call => call[0] === 'DeviceSessionGet')
|
|
2199
|
+
.every(call => !('seed_domains' in (call[2] ?? {})))
|
|
2200
|
+
).toBe(true);
|
|
2201
|
+
});
|
|
2202
|
+
|
|
2203
|
+
test('uses empty AskPassphrase after selecting Attach PIN for Cardano intent', async () => {
|
|
2204
|
+
const typedCall = jest.fn((request: string) => {
|
|
2205
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2206
|
+
return { message: { version: 2 } };
|
|
2207
|
+
}
|
|
2208
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
2209
|
+
return { message: {} };
|
|
2210
|
+
}
|
|
2211
|
+
if (request === 'DeviceSessionGet') {
|
|
2212
|
+
return {
|
|
2213
|
+
message: {
|
|
2214
|
+
btc_test_address: 'attach-state',
|
|
2215
|
+
session_id: 'attach-session',
|
|
2216
|
+
seed_domains: CARDANO_SEED_DOMAINS,
|
|
2217
|
+
},
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2221
|
+
});
|
|
2222
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ attachPinOnDevice: true });
|
|
2223
|
+
const device = createDevice({ typedCall, promptPassphrase });
|
|
2224
|
+
device.features.attachToPinEnabled = true;
|
|
2225
|
+
|
|
2226
|
+
await expect(
|
|
2227
|
+
getProtocolV2WalletSession(device as any, {
|
|
2228
|
+
forceWalletSelection: true,
|
|
2229
|
+
deriveCardano: true,
|
|
2230
|
+
})
|
|
2231
|
+
).resolves.toMatchObject({
|
|
2232
|
+
passphraseState: 'attach-state',
|
|
2233
|
+
newSession: 'attach-session',
|
|
2234
|
+
unlockedAttachPin: true,
|
|
2235
|
+
});
|
|
2236
|
+
expect(promptPassphrase).toHaveBeenCalledTimes(1);
|
|
2237
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(
|
|
2238
|
+
DeviceSessionPinType.AttachToPin,
|
|
2239
|
+
expect.objectContaining({ emitUiEvent: false })
|
|
2240
|
+
);
|
|
2241
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
2242
|
+
passphrase: '',
|
|
2243
|
+
on_device: false,
|
|
2244
|
+
seed_domains: CARDANO_SEED_DOMAINS,
|
|
2245
|
+
});
|
|
2246
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
2247
|
+
expect(
|
|
2248
|
+
typedCall.mock.calls
|
|
2249
|
+
.filter(call => call[0] === 'DeviceSessionGet')
|
|
2250
|
+
.every(call => !('seed_domains' in (call[2] ?? {})))
|
|
2251
|
+
).toBe(true);
|
|
2252
|
+
});
|
|
2253
|
+
|
|
2254
|
+
test('still locks before a passphrase picker after Attach PIN Cardano empty Ask', async () => {
|
|
2255
|
+
const typedCall = jest.fn((request: string) => {
|
|
2256
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2257
|
+
return { message: { version: 2 } };
|
|
2258
|
+
}
|
|
2259
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
2260
|
+
return { message: {} };
|
|
2261
|
+
}
|
|
2262
|
+
if (request === 'DeviceSessionGet') {
|
|
2263
|
+
return {
|
|
2264
|
+
message: {
|
|
2265
|
+
btc_test_address: 'attach-state',
|
|
2266
|
+
session_id: 'attach-session',
|
|
2267
|
+
seed_domains: CARDANO_SEED_DOMAINS,
|
|
2268
|
+
},
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2272
|
+
});
|
|
2273
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'should-not-ask' });
|
|
2274
|
+
const device = createDevice({
|
|
2275
|
+
typedCall,
|
|
2276
|
+
promptPassphrase,
|
|
2277
|
+
unlockedAttachPin: true,
|
|
2278
|
+
});
|
|
2279
|
+
|
|
2280
|
+
await getProtocolV2WalletSession(device as any, {
|
|
2281
|
+
readCurrentAttachPinSession: true,
|
|
2282
|
+
deriveCardano: true,
|
|
2283
|
+
});
|
|
2284
|
+
await expect(
|
|
2285
|
+
getProtocolV2WalletSession(device as any, { forceWalletSelection: true })
|
|
2286
|
+
).rejects.toMatchObject({
|
|
2287
|
+
errorCode: HardwareErrorCode.DeviceCheckUnlockTypeError,
|
|
2288
|
+
});
|
|
2289
|
+
expect(device.lockDevice).toHaveBeenCalled();
|
|
2290
|
+
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
2291
|
+
});
|
|
2292
|
+
|
|
2293
|
+
test('uses a second Get for Cardano when passphrase protection is off', async () => {
|
|
2294
|
+
const typedCall = jest.fn((request: string) => {
|
|
2295
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2296
|
+
return { message: { version: 2 } };
|
|
2297
|
+
}
|
|
2298
|
+
if (request === 'DeviceSessionGet') {
|
|
2299
|
+
const getCount = typedCall.mock.calls.filter(call => call[0] === 'DeviceSessionGet').length;
|
|
2300
|
+
return {
|
|
2301
|
+
message: {
|
|
2302
|
+
btc_test_address: 'standard-state',
|
|
2303
|
+
session_id: 'standard-session',
|
|
2304
|
+
seed_domains: getCount > 1 ? CARDANO_SEED_DOMAINS : STANDARD_SEED_DOMAINS,
|
|
2305
|
+
},
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2309
|
+
});
|
|
2310
|
+
const device = createDevice({
|
|
2311
|
+
typedCall,
|
|
2312
|
+
passphraseProtection: false,
|
|
2313
|
+
});
|
|
2314
|
+
|
|
2315
|
+
await expect(
|
|
2316
|
+
getProtocolV2WalletSession(device as any, {
|
|
2317
|
+
onlyMainPin: true,
|
|
2318
|
+
deriveCardano: true,
|
|
2319
|
+
})
|
|
2320
|
+
).resolves.toMatchObject({
|
|
2321
|
+
passphraseState: 'standard-state',
|
|
2322
|
+
newSession: 'standard-session',
|
|
2323
|
+
});
|
|
2324
|
+
expect(typedCall).not.toHaveBeenCalledWith(
|
|
2325
|
+
'DeviceSessionAskPassphrase',
|
|
2326
|
+
'Success',
|
|
2327
|
+
expect.anything()
|
|
2328
|
+
);
|
|
2329
|
+
expect(typedCall.mock.calls.filter(call => call[0] === 'DeviceSessionGet')).toHaveLength(2);
|
|
2330
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
2331
|
+
});
|
|
2332
|
+
|
|
2333
|
+
test('fails closed when Cardano fallback still lacks Cardano', async () => {
|
|
2334
|
+
const typedCall = jest.fn((request: string) => {
|
|
2335
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2336
|
+
return { message: { version: 2 } };
|
|
2337
|
+
}
|
|
2338
|
+
if (request === 'DeviceSessionAskPassphrase') {
|
|
2339
|
+
return { message: {} };
|
|
2340
|
+
}
|
|
2341
|
+
if (request === 'DeviceSessionGet') {
|
|
2342
|
+
return {
|
|
2343
|
+
message: {
|
|
2344
|
+
btc_test_address: 'hidden-state',
|
|
2345
|
+
session_id: 'hidden-session',
|
|
2346
|
+
seed_domains: STANDARD_SEED_DOMAINS,
|
|
2347
|
+
},
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2351
|
+
});
|
|
2352
|
+
const device = createDevice({
|
|
2353
|
+
typedCall,
|
|
2354
|
+
promptPassphrase: jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' }),
|
|
2355
|
+
});
|
|
2356
|
+
deviceWalletSessionStore.set('device-1', 'hidden-state', 'cached-session');
|
|
2357
|
+
|
|
2358
|
+
await expect(
|
|
2359
|
+
getProtocolV2WalletSession(device as any, {
|
|
2360
|
+
expectedPassphraseState: 'hidden-state',
|
|
2361
|
+
deriveCardano: true,
|
|
2362
|
+
})
|
|
2363
|
+
).rejects.toMatchObject({
|
|
2364
|
+
errorCode: HardwareErrorCode.WalletSessionInvalid,
|
|
2365
|
+
});
|
|
2366
|
+
expect(device.clearInternalState).toHaveBeenCalled();
|
|
2367
|
+
});
|
|
2368
|
+
|
|
2369
|
+
test('locks before selecting a passphrase wallet from an Attach PIN session', async () => {
|
|
2370
|
+
const typedCall = jest.fn((request: string) => {
|
|
2371
|
+
if (request === 'ProtocolInfoRequest') {
|
|
2372
|
+
return { message: { version: 2 } };
|
|
2373
|
+
}
|
|
2374
|
+
if (request === 'DeviceStatusGet') {
|
|
2375
|
+
return {
|
|
2376
|
+
message: {
|
|
2377
|
+
device_id: 'device-1',
|
|
2378
|
+
unlocked: true,
|
|
2379
|
+
passphrase_enabled: true,
|
|
2380
|
+
unlocked_by_attach_to_pin: true,
|
|
2381
|
+
},
|
|
2382
|
+
};
|
|
2383
|
+
}
|
|
2384
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
2385
|
+
});
|
|
2386
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'should-not-ask' });
|
|
2387
|
+
const device = createDevice({
|
|
2388
|
+
typedCall,
|
|
2389
|
+
promptPassphrase,
|
|
2390
|
+
unlockedAttachPin: true,
|
|
2391
|
+
});
|
|
2392
|
+
|
|
2393
|
+
await expect(
|
|
2394
|
+
getProtocolV2WalletSession(device as any, { forceWalletSelection: true })
|
|
2395
|
+
).rejects.toMatchObject({
|
|
2396
|
+
errorCode: HardwareErrorCode.DeviceCheckUnlockTypeError,
|
|
2397
|
+
});
|
|
2398
|
+
expect(device.lockDevice).toHaveBeenCalled();
|
|
2399
|
+
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
2400
|
+
expect(typedCall).not.toHaveBeenCalledWith(
|
|
2401
|
+
'DeviceSessionAskPassphrase',
|
|
2402
|
+
'Success',
|
|
2403
|
+
expect.anything()
|
|
2404
|
+
);
|
|
2405
|
+
});
|
|
2406
|
+
|
|
2109
2407
|
test('asks Standard-only seed domains when deriveCardano is false', async () => {
|
|
2110
2408
|
const typedCall = jest.fn((request: string) => {
|
|
2111
2409
|
if (request === 'ProtocolInfoRequest') {
|
|
@@ -2235,7 +2235,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2235
2235
|
firmwareVersion: '4.15.0',
|
|
2236
2236
|
passphraseProtection: true,
|
|
2237
2237
|
sessionId: 'feature-session',
|
|
2238
|
-
unlockedAttachPin:
|
|
2238
|
+
unlockedAttachPin: false,
|
|
2239
2239
|
};
|
|
2240
2240
|
const typedCall = jest
|
|
2241
2241
|
.fn()
|