@onekeyfe/hd-core 1.2.0-alpha.36 → 1.2.0-alpha.39
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 +5 -1
- package/__tests__/deviceUploadNft.test.ts +187 -0
- package/__tests__/homescreen.test.ts +41 -1
- package/__tests__/logBlockEvent.test.ts +1 -1
- package/__tests__/open-wallet-session.test.ts +161 -44
- package/__tests__/pro2Nft.test.ts +108 -0
- package/__tests__/protocol-v2-unlock-policy.test.ts +86 -35
- package/__tests__/protocol-v2.test.ts +168 -444
- package/__tests__/public-pro2-api-boundary.test.ts +1 -0
- package/__tests__/uiProgressThrottle.test.ts +74 -0
- package/dist/api/BaseMethod.d.ts +3 -1
- package/dist/api/BaseMethod.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/firmware/FirmwareUpdateBaseMethod.d.ts +0 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts +2 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +30 -0
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +34 -2
- package/dist/index.js +544 -496
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +2 -1
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +3 -0
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/utils/homescreen.d.ts +4 -0
- package/dist/utils/homescreen.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.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/pro2Nft.d.ts +29 -0
- package/dist/utils/pro2Nft.d.ts.map +1 -0
- package/dist/utils/pro2Wallpaper.d.ts +10 -0
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/dist/utils/uiProgressThrottle.d.ts +3 -0
- package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +7 -9
- package/src/api/OpenWalletSession.ts +37 -15
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +29 -30
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +0 -7
- package/src/api/helpers/protocolV2FileWrite.ts +11 -4
- package/src/api/index.ts +1 -0
- package/src/api/protocol-v2/DeviceUploadNft.ts +166 -0
- package/src/core/index.ts +83 -82
- package/src/data/messages/messages-protocol-v2.json +10 -15
- package/src/events/logBlockEvent.ts +1 -0
- package/src/inject.ts +1 -0
- package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +117 -0
- package/src/protocols/protocol-v2/walletSession.ts +18 -51
- package/src/types/api/index.ts +2 -0
- package/src/types/api/protocolV2.ts +13 -0
- package/src/utils/homescreen.ts +32 -6
- package/src/utils/index.ts +6 -1
- package/src/utils/pro2Nft.ts +91 -0
- package/src/utils/pro2Wallpaper.ts +35 -8
- package/src/utils/uiProgressThrottle.ts +63 -0
- package/dist/api/firmware/progressThrottle.d.ts +0 -3
- package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
- package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
- package/src/api/firmware/progressThrottle.ts +0 -44
- package/src/protocols/protocol-v2/lockedError.ts +0 -10
- package/src/protocols/protocol-v2/unlockRetry.ts +0 -131
|
@@ -79,7 +79,10 @@ import {
|
|
|
79
79
|
getProtocolV2WalletSession,
|
|
80
80
|
refreshProtocolV2DeviceStatus,
|
|
81
81
|
} from '../src/protocols/protocol-v2/walletSession';
|
|
82
|
-
import {
|
|
82
|
+
import {
|
|
83
|
+
createProtocolV2UnlockContext,
|
|
84
|
+
runMethodWithUnlockPolicy,
|
|
85
|
+
} from '../src/protocols/protocol-v2/unlockPolicyRunner';
|
|
83
86
|
import { BaseMethod } from '../src/api/BaseMethod';
|
|
84
87
|
import {
|
|
85
88
|
buildProtocolV1FeaturesPayload,
|
|
@@ -241,7 +244,7 @@ describe('UploadPortfolio', () => {
|
|
|
241
244
|
(method as any).device = device;
|
|
242
245
|
|
|
243
246
|
method.init();
|
|
244
|
-
await
|
|
247
|
+
await runMethodWithUnlockPolicy(method, device as any);
|
|
245
248
|
|
|
246
249
|
expect(unlockDevice).not.toHaveBeenCalled();
|
|
247
250
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
@@ -868,7 +871,6 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
868
871
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
869
872
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
870
873
|
session_id: 'standard-session',
|
|
871
|
-
btc_test_address: 'standard-state',
|
|
872
874
|
});
|
|
873
875
|
expect(deviceWalletSessionStore.getStandard(deviceId)).toEqual({
|
|
874
876
|
passphraseState: 'standard-state',
|
|
@@ -1338,7 +1340,6 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1338
1340
|
|
|
1339
1341
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1340
1342
|
session_id: 'session-a',
|
|
1341
|
-
btc_test_address: 'state-a',
|
|
1342
1343
|
});
|
|
1343
1344
|
expect(device.getInternalState()).toBe('session-b');
|
|
1344
1345
|
});
|
|
@@ -1406,11 +1407,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1406
1407
|
});
|
|
1407
1408
|
expect(typedCall.mock.calls).toEqual([
|
|
1408
1409
|
['ProtocolInfoRequest', 'ProtocolInfo', { eventless_wallet_session: true }],
|
|
1409
|
-
[
|
|
1410
|
-
'DeviceSessionGet',
|
|
1411
|
-
'DeviceSession',
|
|
1412
|
-
{ session_id: 'session-a', btc_test_address: 'state-a' },
|
|
1413
|
-
],
|
|
1410
|
+
['DeviceSessionGet', 'DeviceSession', { session_id: 'session-a' }],
|
|
1414
1411
|
]);
|
|
1415
1412
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1416
1413
|
expect(device.getInternalState()).toBeUndefined();
|
|
@@ -1618,9 +1615,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1618
1615
|
});
|
|
1619
1616
|
|
|
1620
1617
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1621
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1622
|
-
btc_test_address: 'expected-state',
|
|
1623
|
-
});
|
|
1618
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
1624
1619
|
expect(typedCall).toHaveBeenCalledWith('LockDevice', 'Success', {});
|
|
1625
1620
|
expect(typedCall.mock.calls.filter(call => call[0] === 'DeviceSessionGet')).toHaveLength(1);
|
|
1626
1621
|
expect(device.getInternalState()).toBeUndefined();
|
|
@@ -2008,9 +2003,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2008
2003
|
await expect(
|
|
2009
2004
|
getProtocolV2WalletSession(device, { expectedPassphraseState: 'expected-state' })
|
|
2010
2005
|
).resolves.toMatchObject({ passphraseState: 'expected-state' });
|
|
2011
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
2012
|
-
btc_test_address: 'expected-state',
|
|
2013
|
-
});
|
|
2006
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
2014
2007
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
2015
2008
|
});
|
|
2016
2009
|
|
|
@@ -2077,11 +2070,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2077
2070
|
true
|
|
2078
2071
|
);
|
|
2079
2072
|
expect(typedCall.mock.calls).toEqual([
|
|
2080
|
-
[
|
|
2081
|
-
'DeviceSessionGet',
|
|
2082
|
-
'DeviceSession',
|
|
2083
|
-
{ session_id: 'session-pro2-app', btc_test_address: 'state-pro2-app' },
|
|
2084
|
-
],
|
|
2073
|
+
['DeviceSessionGet', 'DeviceSession', { session_id: 'session-pro2-app' }],
|
|
2085
2074
|
]);
|
|
2086
2075
|
});
|
|
2087
2076
|
|
|
@@ -2348,9 +2337,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2348
2337
|
);
|
|
2349
2338
|
|
|
2350
2339
|
expect(device.getInternalState()).toBeUndefined();
|
|
2351
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
2352
|
-
btc_test_address: 'expected-state',
|
|
2353
|
-
});
|
|
2340
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
2354
2341
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
2355
2342
|
passphrase: 'host hidden wallet',
|
|
2356
2343
|
on_device: false,
|
|
@@ -6206,334 +6193,230 @@ describe('Protocol V2 protected method execution', () => {
|
|
|
6206
6193
|
expect(method.unlockPolicy).toBe('none');
|
|
6207
6194
|
});
|
|
6208
6195
|
|
|
6209
|
-
test('
|
|
6196
|
+
test('checks fresh status for a wallet business method without a name allowlist', async () => {
|
|
6210
6197
|
const calls: string[] = [];
|
|
6198
|
+
const features = { unlocked: true };
|
|
6211
6199
|
const method = {
|
|
6212
|
-
name: '
|
|
6213
|
-
unlockPolicy: '
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
.
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
})
|
|
6221
|
-
.mockImplementationOnce(() => {
|
|
6222
|
-
calls.push('run-2');
|
|
6223
|
-
return Promise.resolve({ message: 'ok' });
|
|
6224
|
-
}),
|
|
6200
|
+
name: 'confluxSignMessageCIP23',
|
|
6201
|
+
unlockPolicy: 'none',
|
|
6202
|
+
useDevicePassphraseState: true,
|
|
6203
|
+
protocolV2UiInteraction: { reason: 'address-confirmation' },
|
|
6204
|
+
run: jest.fn(() => {
|
|
6205
|
+
calls.push('run');
|
|
6206
|
+
return Promise.resolve({ address: '0x1' });
|
|
6207
|
+
}),
|
|
6225
6208
|
};
|
|
6226
6209
|
const device = {
|
|
6210
|
+
features,
|
|
6211
|
+
commands: {
|
|
6212
|
+
typedCall: jest.fn(() => {
|
|
6213
|
+
calls.push('status');
|
|
6214
|
+
return Promise.resolve({ message: { unlocked: true } });
|
|
6215
|
+
}),
|
|
6216
|
+
},
|
|
6227
6217
|
isProtocolV2: () => true,
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6218
|
+
isBootloader: () => false,
|
|
6219
|
+
isRomloader: () => false,
|
|
6220
|
+
updateProtocolV2Status: jest.fn((status: { unlocked?: boolean }) => {
|
|
6221
|
+
features.unlocked = status.unlocked === true;
|
|
6222
|
+
return features;
|
|
6231
6223
|
}),
|
|
6224
|
+
unlockDevice: jest.fn(),
|
|
6232
6225
|
};
|
|
6233
6226
|
const uiCoordinator = {
|
|
6234
6227
|
enterMethodInteraction: jest.fn(() => calls.push('method-prompt')),
|
|
6235
|
-
enterUnlockInteraction: jest.fn(
|
|
6236
|
-
resumeMethodInteraction: jest.fn(() => calls.push('method-prompt')),
|
|
6228
|
+
enterUnlockInteraction: jest.fn(),
|
|
6237
6229
|
};
|
|
6238
6230
|
|
|
6239
6231
|
await expect(
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
]);
|
|
6232
|
+
runMethodWithUnlockPolicy(method as any, device as any, {
|
|
6233
|
+
uiCoordinator: uiCoordinator as any,
|
|
6234
|
+
})
|
|
6235
|
+
).resolves.toEqual({ address: '0x1' });
|
|
6236
|
+
|
|
6237
|
+
expect(calls).toEqual(['status', 'method-prompt', 'run']);
|
|
6238
|
+
expect(device.commands.typedCall).toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
|
|
6239
|
+
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
6240
|
+
expect(method.run).toHaveBeenCalledTimes(1);
|
|
6250
6241
|
});
|
|
6251
6242
|
|
|
6252
|
-
test('
|
|
6243
|
+
test('validates the fresh device identity before starting unlock', async () => {
|
|
6253
6244
|
const calls: string[] = [];
|
|
6245
|
+
const identityError = new Error('Unexpected device');
|
|
6254
6246
|
const method = {
|
|
6255
6247
|
name: 'evmSignMessage',
|
|
6256
|
-
|
|
6248
|
+
unlockPolicy: 'none',
|
|
6257
6249
|
useDevicePassphraseState: true,
|
|
6258
|
-
|
|
6259
|
-
run: jest
|
|
6260
|
-
.fn()
|
|
6261
|
-
.mockImplementationOnce(() => {
|
|
6262
|
-
calls.push('run-1');
|
|
6263
|
-
return Promise.reject(deviceLockedError());
|
|
6264
|
-
})
|
|
6265
|
-
.mockImplementationOnce(() => {
|
|
6266
|
-
calls.push('run-2');
|
|
6267
|
-
return Promise.resolve({ message: 'ok' });
|
|
6268
|
-
}),
|
|
6250
|
+
run: jest.fn(),
|
|
6269
6251
|
};
|
|
6270
|
-
const
|
|
6271
|
-
if (requestType === 'ProtocolInfoRequest') {
|
|
6272
|
-
calls.push('negotiate-session');
|
|
6273
|
-
return Promise.resolve({ message: { version: 2 } });
|
|
6274
|
-
}
|
|
6275
|
-
if (requestType === 'DeviceSessionGet') {
|
|
6276
|
-
calls.push('resume-hidden-session');
|
|
6277
|
-
expect(request).toEqual({
|
|
6278
|
-
session_id: 'hidden-session',
|
|
6279
|
-
btc_test_address: 'hidden-state',
|
|
6280
|
-
});
|
|
6281
|
-
return Promise.resolve({
|
|
6282
|
-
message: {
|
|
6283
|
-
session_id: 'hidden-session',
|
|
6284
|
-
btc_test_address: 'hidden-state',
|
|
6285
|
-
},
|
|
6286
|
-
});
|
|
6287
|
-
}
|
|
6288
|
-
throw new Error(`Unexpected request: ${requestType}`);
|
|
6289
|
-
});
|
|
6252
|
+
const features = { unlocked: false };
|
|
6290
6253
|
const device = {
|
|
6291
|
-
features
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6254
|
+
features,
|
|
6255
|
+
commands: {
|
|
6256
|
+
typedCall: jest.fn(() => {
|
|
6257
|
+
calls.push('status');
|
|
6258
|
+
return Promise.resolve({ message: { unlocked: false } });
|
|
6259
|
+
}),
|
|
6260
|
+
},
|
|
6298
6261
|
isProtocolV2: () => true,
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
getCurrentPassphraseProtection: () => true,
|
|
6304
|
-
getInternalState: () => 'hidden-session',
|
|
6305
|
-
clearInternalState: jest.fn(),
|
|
6306
|
-
getCurrentDeviceId: () => 'wallet-device-id',
|
|
6307
|
-
updateInternalState: jest.fn(() => calls.push('validate-hidden-session')),
|
|
6262
|
+
isBootloader: () => false,
|
|
6263
|
+
isRomloader: () => false,
|
|
6264
|
+
updateProtocolV2Status: jest.fn(() => features),
|
|
6265
|
+
unlockDevice: jest.fn(),
|
|
6308
6266
|
};
|
|
6309
6267
|
|
|
6310
|
-
await expect(
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
'validate-hidden-session',
|
|
6319
|
-
'run-2',
|
|
6320
|
-
]);
|
|
6321
|
-
});
|
|
6322
|
-
|
|
6323
|
-
test('does not replay a business method when the hidden-wallet session cannot be restored after unlock', async () => {
|
|
6324
|
-
const initialError = deviceLockedError();
|
|
6325
|
-
const restoreError = new Error('Invalid wallet session');
|
|
6326
|
-
const method = {
|
|
6327
|
-
name: 'evmSignMessage',
|
|
6328
|
-
payload: { passphraseState: 'hidden-state' },
|
|
6329
|
-
useDevicePassphraseState: true,
|
|
6330
|
-
unlockPolicy: 'retry-on-locked',
|
|
6331
|
-
run: jest.fn().mockRejectedValueOnce(initialError),
|
|
6332
|
-
};
|
|
6333
|
-
const typedCall = jest.fn((requestType: string) => {
|
|
6334
|
-
if (requestType === 'ProtocolInfoRequest') {
|
|
6335
|
-
return Promise.resolve({ message: { version: 2 } });
|
|
6336
|
-
}
|
|
6337
|
-
if (requestType === 'DeviceSessionGet') {
|
|
6338
|
-
return Promise.reject(restoreError);
|
|
6339
|
-
}
|
|
6340
|
-
throw new Error(`Unexpected request: ${requestType}`);
|
|
6341
|
-
});
|
|
6342
|
-
const device = {
|
|
6343
|
-
features: { unlocked: true, passphraseProtection: true },
|
|
6344
|
-
passphraseState: 'hidden-state',
|
|
6345
|
-
commands: { typedCall },
|
|
6346
|
-
ensureProtocolV2RuntimeContext: () =>
|
|
6347
|
-
typedCall('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
6348
|
-
eventless_wallet_session: true,
|
|
6349
|
-
}).then(response => response.message),
|
|
6350
|
-
isProtocolV2: () => true,
|
|
6351
|
-
unlockDevice: jest.fn().mockResolvedValue(undefined),
|
|
6352
|
-
getCurrentPassphraseProtection: () => true,
|
|
6353
|
-
getInternalState: () => 'hidden-session',
|
|
6354
|
-
clearInternalState: jest.fn(),
|
|
6355
|
-
getCurrentDeviceId: () => 'wallet-device-id',
|
|
6356
|
-
updateInternalState: jest.fn(),
|
|
6357
|
-
};
|
|
6268
|
+
await expect(
|
|
6269
|
+
runMethodWithUnlockPolicy(method as any, device as any, {
|
|
6270
|
+
afterStatusBeforeUnlock: () => {
|
|
6271
|
+
calls.push('identity');
|
|
6272
|
+
throw identityError;
|
|
6273
|
+
},
|
|
6274
|
+
})
|
|
6275
|
+
).rejects.toBe(identityError);
|
|
6358
6276
|
|
|
6359
|
-
|
|
6360
|
-
expect(
|
|
6361
|
-
expect(
|
|
6362
|
-
expect(device.clearInternalState).toHaveBeenCalledTimes(1);
|
|
6363
|
-
expect(device.updateInternalState).not.toHaveBeenCalled();
|
|
6277
|
+
expect(calls).toEqual(['status', 'identity']);
|
|
6278
|
+
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
6279
|
+
expect(method.run).not.toHaveBeenCalled();
|
|
6364
6280
|
});
|
|
6365
6281
|
|
|
6366
|
-
test('
|
|
6282
|
+
test('unlocks before business and never replays the callback', async () => {
|
|
6367
6283
|
const calls: string[] = [];
|
|
6284
|
+
const features = { unlocked: true };
|
|
6368
6285
|
const method = {
|
|
6369
6286
|
name: 'evmSignMessage',
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
unlockPolicy: 'retry-on-locked',
|
|
6287
|
+
unlockPolicy: 'unlock-before-run',
|
|
6288
|
+
protocolV2UiInteraction: { reason: 'signing-confirmation' },
|
|
6373
6289
|
run: jest.fn(() => {
|
|
6374
6290
|
calls.push('run');
|
|
6375
|
-
return Promise.resolve({
|
|
6291
|
+
return Promise.resolve({ signature: 'signed' });
|
|
6376
6292
|
}),
|
|
6377
6293
|
};
|
|
6378
|
-
const typedCall = jest.fn((requestType: string, _responseType: string, request: any) => {
|
|
6379
|
-
if (requestType === 'ProtocolInfoRequest') {
|
|
6380
|
-
calls.push('negotiate-session');
|
|
6381
|
-
return Promise.resolve({ message: { version: 2 } });
|
|
6382
|
-
}
|
|
6383
|
-
if (requestType === 'DeviceSessionGet') {
|
|
6384
|
-
calls.push('resume-hidden-session');
|
|
6385
|
-
expect(request).toEqual({
|
|
6386
|
-
session_id: 'hidden-session',
|
|
6387
|
-
btc_test_address: 'hidden-state',
|
|
6388
|
-
});
|
|
6389
|
-
return Promise.resolve({
|
|
6390
|
-
message: {
|
|
6391
|
-
session_id: 'hidden-session',
|
|
6392
|
-
btc_test_address: 'hidden-state',
|
|
6393
|
-
},
|
|
6394
|
-
});
|
|
6395
|
-
}
|
|
6396
|
-
throw new Error(`Unexpected request: ${requestType}`);
|
|
6397
|
-
});
|
|
6398
|
-
const features = {
|
|
6399
|
-
unlocked: false,
|
|
6400
|
-
unlockedAttachPin: true,
|
|
6401
|
-
passphraseProtection: true,
|
|
6402
|
-
};
|
|
6403
6294
|
const device = {
|
|
6404
6295
|
features,
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6296
|
+
commands: {
|
|
6297
|
+
typedCall: jest.fn(() => {
|
|
6298
|
+
calls.push('status');
|
|
6299
|
+
return Promise.resolve({ message: { unlocked: false } });
|
|
6300
|
+
}),
|
|
6301
|
+
},
|
|
6411
6302
|
isProtocolV2: () => true,
|
|
6303
|
+
isBootloader: () => false,
|
|
6304
|
+
isRomloader: () => false,
|
|
6305
|
+
updateProtocolV2Status: jest.fn((status: { unlocked?: boolean }) => {
|
|
6306
|
+
features.unlocked = status.unlocked === true;
|
|
6307
|
+
return features;
|
|
6308
|
+
}),
|
|
6412
6309
|
unlockDevice: jest.fn(() => {
|
|
6413
|
-
calls.push('unlock
|
|
6310
|
+
calls.push('unlock');
|
|
6414
6311
|
features.unlocked = true;
|
|
6415
|
-
features
|
|
6416
|
-
|
|
6312
|
+
return Promise.resolve(features);
|
|
6313
|
+
}),
|
|
6314
|
+
};
|
|
6315
|
+
const uiCoordinator = {
|
|
6316
|
+
enterMethodInteraction: jest.fn(() => calls.push('method-prompt')),
|
|
6317
|
+
enterUnlockInteraction: jest.fn(() => {
|
|
6318
|
+
calls.push('unlock-prompt');
|
|
6319
|
+
return undefined;
|
|
6417
6320
|
}),
|
|
6418
|
-
getCurrentPassphraseProtection: () => true,
|
|
6419
|
-
getInternalState: () => 'hidden-session',
|
|
6420
|
-
clearInternalState: jest.fn(),
|
|
6421
|
-
getCurrentDeviceId: () => 'wallet-device-id',
|
|
6422
|
-
updateInternalState: jest.fn(() => calls.push('validate-hidden-session')),
|
|
6423
6321
|
};
|
|
6424
6322
|
|
|
6425
|
-
await expect(
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
});
|
|
6323
|
+
await expect(
|
|
6324
|
+
runMethodWithUnlockPolicy(method as any, device as any, {
|
|
6325
|
+
uiCoordinator: uiCoordinator as any,
|
|
6326
|
+
prepare: () => {
|
|
6327
|
+
calls.push('wallet-session');
|
|
6328
|
+
return Promise.resolve();
|
|
6329
|
+
},
|
|
6330
|
+
})
|
|
6331
|
+
).resolves.toEqual({ signature: 'signed' });
|
|
6332
|
+
|
|
6434
6333
|
expect(calls).toEqual([
|
|
6435
|
-
'
|
|
6436
|
-
'
|
|
6437
|
-
'
|
|
6438
|
-
'
|
|
6334
|
+
'status',
|
|
6335
|
+
'unlock-prompt',
|
|
6336
|
+
'unlock',
|
|
6337
|
+
'wallet-session',
|
|
6338
|
+
'method-prompt',
|
|
6439
6339
|
'run',
|
|
6440
6340
|
]);
|
|
6341
|
+
expect(device.unlockDevice).toHaveBeenCalledTimes(1);
|
|
6342
|
+
expect(method.run).toHaveBeenCalledTimes(1);
|
|
6441
6343
|
});
|
|
6442
6344
|
|
|
6443
|
-
test('
|
|
6345
|
+
test('fails closed when fresh status does not explicitly report unlocked state', async () => {
|
|
6444
6346
|
const method = {
|
|
6445
6347
|
name: 'evmGetAddress',
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
unlockPolicy: 'retry-on-locked',
|
|
6449
|
-
run: jest.fn().mockResolvedValue({ address: 'standard-wallet-address' }),
|
|
6348
|
+
unlockPolicy: 'unlock-before-run',
|
|
6349
|
+
run: jest.fn(),
|
|
6450
6350
|
};
|
|
6451
6351
|
const device = {
|
|
6452
|
-
features: { unlocked:
|
|
6453
|
-
|
|
6352
|
+
features: { unlocked: undefined },
|
|
6353
|
+
commands: { typedCall: jest.fn().mockResolvedValue({ message: {} }) },
|
|
6454
6354
|
isProtocolV2: () => true,
|
|
6455
|
-
|
|
6355
|
+
isBootloader: () => false,
|
|
6356
|
+
isRomloader: () => false,
|
|
6357
|
+
updateProtocolV2Status: jest.fn(() => ({ unlocked: undefined })),
|
|
6358
|
+
unlockDevice: jest.fn(),
|
|
6456
6359
|
};
|
|
6457
6360
|
|
|
6458
|
-
await expect(
|
|
6459
|
-
|
|
6361
|
+
await expect(runMethodWithUnlockPolicy(method as any, device as any)).rejects.toMatchObject({
|
|
6362
|
+
errorCode: HardwareErrorCode.RuntimeError,
|
|
6460
6363
|
});
|
|
6461
|
-
expect(device.unlockDevice).
|
|
6462
|
-
expect(method.run).
|
|
6364
|
+
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
6365
|
+
expect(method.run).not.toHaveBeenCalled();
|
|
6463
6366
|
});
|
|
6464
6367
|
|
|
6465
|
-
test('
|
|
6466
|
-
const
|
|
6368
|
+
test('returns a business DeviceLocked error without unlocking or replaying', async () => {
|
|
6369
|
+
const error = deviceLockedError();
|
|
6467
6370
|
const method = {
|
|
6468
|
-
name: '
|
|
6371
|
+
name: 'evmSignMessage',
|
|
6469
6372
|
unlockPolicy: 'unlock-before-run',
|
|
6470
|
-
|
|
6471
|
-
run: jest.fn(() => {
|
|
6472
|
-
calls.push('run');
|
|
6473
|
-
return Promise.resolve({ message: 'ok' });
|
|
6474
|
-
}),
|
|
6373
|
+
run: jest.fn().mockRejectedValue(error),
|
|
6475
6374
|
};
|
|
6476
6375
|
const features = { unlocked: true };
|
|
6477
6376
|
const device = {
|
|
6478
6377
|
features,
|
|
6479
|
-
commands: {
|
|
6480
|
-
typedCall: jest.fn(() => {
|
|
6481
|
-
calls.push('refresh-status');
|
|
6482
|
-
return Promise.resolve({ message: { unlocked: false } });
|
|
6483
|
-
}),
|
|
6484
|
-
},
|
|
6378
|
+
commands: { typedCall: jest.fn().mockResolvedValue({ message: { unlocked: true } }) },
|
|
6485
6379
|
isProtocolV2: () => true,
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
unlockDevice: jest.fn(() => {
|
|
6491
|
-
calls.push('unlock');
|
|
6492
|
-
features.unlocked = true;
|
|
6493
|
-
return Promise.resolve();
|
|
6494
|
-
}),
|
|
6495
|
-
};
|
|
6496
|
-
const uiCoordinator = {
|
|
6497
|
-
enterMethodInteraction: jest.fn(() => calls.push('method-prompt')),
|
|
6498
|
-
enterUnlockInteraction: jest.fn(() => calls.push('unlock-prompt')),
|
|
6499
|
-
resumeMethodInteraction: jest.fn(() => calls.push('method-prompt')),
|
|
6380
|
+
isBootloader: () => false,
|
|
6381
|
+
isRomloader: () => false,
|
|
6382
|
+
updateProtocolV2Status: jest.fn(() => features),
|
|
6383
|
+
unlockDevice: jest.fn(),
|
|
6500
6384
|
};
|
|
6501
6385
|
|
|
6502
|
-
await expect(
|
|
6503
|
-
|
|
6504
|
-
).resolves.toEqual({ message: 'ok' });
|
|
6505
|
-
expect(calls).toEqual(['refresh-status', 'unlock-prompt', 'unlock', 'method-prompt', 'run']);
|
|
6506
|
-
expect(device.commands.typedCall).toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
|
|
6386
|
+
await expect(runMethodWithUnlockPolicy(method as any, device as any)).rejects.toBe(error);
|
|
6387
|
+
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
6507
6388
|
expect(method.run).toHaveBeenCalledTimes(1);
|
|
6508
|
-
expect(uiCoordinator.enterMethodInteraction).toHaveBeenCalledTimes(1);
|
|
6509
|
-
expect(uiCoordinator.resumeMethodInteraction).not.toHaveBeenCalled();
|
|
6510
6389
|
});
|
|
6511
6390
|
|
|
6512
|
-
test('
|
|
6513
|
-
const features = { unlocked:
|
|
6514
|
-
const
|
|
6515
|
-
|
|
6391
|
+
test('reuses one lightweight preflight context across nested protected methods', async () => {
|
|
6392
|
+
const features = { unlocked: true };
|
|
6393
|
+
const context = createProtocolV2UnlockContext();
|
|
6394
|
+
const firstMethod = {
|
|
6395
|
+
name: 'allNetworkGetAddress',
|
|
6516
6396
|
unlockPolicy: 'unlock-before-run',
|
|
6517
|
-
run: jest.fn().mockResolvedValue({
|
|
6397
|
+
run: jest.fn().mockResolvedValue({ root: true }),
|
|
6398
|
+
};
|
|
6399
|
+
const nestedMethod = {
|
|
6400
|
+
name: 'evmGetAddress',
|
|
6401
|
+
unlockPolicy: 'unlock-before-run',
|
|
6402
|
+
run: jest.fn().mockResolvedValue({ address: '0x1' }),
|
|
6518
6403
|
};
|
|
6519
6404
|
const device = {
|
|
6520
6405
|
features,
|
|
6521
|
-
commands: {
|
|
6522
|
-
typedCall: jest.fn().mockResolvedValue({ message: { unlocked: true } }),
|
|
6523
|
-
},
|
|
6406
|
+
commands: { typedCall: jest.fn().mockResolvedValue({ message: { unlocked: true } }) },
|
|
6524
6407
|
isProtocolV2: () => true,
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
}),
|
|
6408
|
+
isBootloader: () => false,
|
|
6409
|
+
isRomloader: () => false,
|
|
6410
|
+
updateProtocolV2Status: jest.fn(() => features),
|
|
6529
6411
|
unlockDevice: jest.fn(),
|
|
6530
6412
|
};
|
|
6531
6413
|
|
|
6532
|
-
await
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
expect(device.
|
|
6536
|
-
expect(
|
|
6414
|
+
await runMethodWithUnlockPolicy(firstMethod as any, device as any, { context });
|
|
6415
|
+
await runMethodWithUnlockPolicy(nestedMethod as any, device as any, { context });
|
|
6416
|
+
|
|
6417
|
+
expect(device.commands.typedCall).toHaveBeenCalledTimes(1);
|
|
6418
|
+
expect(firstMethod.run).toHaveBeenCalledTimes(1);
|
|
6419
|
+
expect(nestedMethod.run).toHaveBeenCalledTimes(1);
|
|
6537
6420
|
});
|
|
6538
6421
|
|
|
6539
6422
|
test.each(['bootloader', 'romloader'] as const)(
|
|
@@ -6553,7 +6436,7 @@ describe('Protocol V2 protected method execution', () => {
|
|
|
6553
6436
|
unlockDevice: jest.fn(),
|
|
6554
6437
|
};
|
|
6555
6438
|
|
|
6556
|
-
await expect(
|
|
6439
|
+
await expect(runMethodWithUnlockPolicy(method as any, device as any)).resolves.toEqual({
|
|
6557
6440
|
message: 'updating',
|
|
6558
6441
|
});
|
|
6559
6442
|
expect(device.commands.typedCall).not.toHaveBeenCalled();
|
|
@@ -6562,189 +6445,30 @@ describe('Protocol V2 protected method execution', () => {
|
|
|
6562
6445
|
}
|
|
6563
6446
|
);
|
|
6564
6447
|
|
|
6565
|
-
test('
|
|
6566
|
-
const method = {
|
|
6567
|
-
name: 'evmSignMessage',
|
|
6568
|
-
params: { message: 'not-exposed-in-event' },
|
|
6569
|
-
payload: {},
|
|
6570
|
-
unlockPolicy: 'retry-on-locked',
|
|
6571
|
-
run: jest.fn().mockResolvedValue({ signature: 'test' }),
|
|
6572
|
-
};
|
|
6573
|
-
const device = {
|
|
6574
|
-
isProtocolV2: () => true,
|
|
6575
|
-
unlockDevice: jest.fn(),
|
|
6576
|
-
};
|
|
6577
|
-
const uiCoordinator = {
|
|
6578
|
-
enterMethodInteraction: jest.fn(),
|
|
6579
|
-
enterUnlockInteraction: jest.fn(),
|
|
6580
|
-
resumeMethodInteraction: jest.fn(),
|
|
6581
|
-
};
|
|
6582
|
-
|
|
6583
|
-
await runMethodWithUnlockRetry(method as any, device as any, uiCoordinator as any);
|
|
6584
|
-
|
|
6585
|
-
expect(uiCoordinator.enterMethodInteraction).toHaveBeenCalledWith({
|
|
6586
|
-
request: 'button',
|
|
6587
|
-
source: 'method-lifecycle',
|
|
6588
|
-
reason: 'signing-confirmation',
|
|
6589
|
-
completion: 'operation-completed',
|
|
6590
|
-
deviceOnly: true,
|
|
6591
|
-
operation: 'evmSignMessage',
|
|
6592
|
-
});
|
|
6593
|
-
});
|
|
6594
|
-
|
|
6595
|
-
test('does not unlock a Protocol V1 device or a method without the policy', async () => {
|
|
6448
|
+
test('keeps Protocol V1 and lock-free methods outside the preflight path', async () => {
|
|
6596
6449
|
for (const [isProtocolV2, unlockPolicy] of [
|
|
6597
|
-
[false, '
|
|
6450
|
+
[false, 'unlock-before-run'],
|
|
6598
6451
|
[true, 'none'],
|
|
6599
6452
|
] as const) {
|
|
6600
|
-
const error = deviceLockedError();
|
|
6601
6453
|
const method = {
|
|
6602
6454
|
unlockPolicy,
|
|
6603
|
-
|
|
6455
|
+
useDevicePassphraseState: false,
|
|
6456
|
+
run: jest.fn().mockResolvedValue({ message: 'ok' }),
|
|
6604
6457
|
};
|
|
6605
6458
|
const device = {
|
|
6459
|
+
commands: { typedCall: jest.fn() },
|
|
6606
6460
|
isProtocolV2: () => isProtocolV2,
|
|
6607
6461
|
unlockDevice: jest.fn(),
|
|
6608
6462
|
};
|
|
6609
6463
|
|
|
6610
|
-
await expect(
|
|
6464
|
+
await expect(runMethodWithUnlockPolicy(method as any, device as any)).resolves.toEqual({
|
|
6465
|
+
message: 'ok',
|
|
6466
|
+
});
|
|
6467
|
+
expect(device.commands.typedCall).not.toHaveBeenCalled();
|
|
6611
6468
|
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
6612
6469
|
expect(method.run).toHaveBeenCalledTimes(1);
|
|
6613
6470
|
}
|
|
6614
6471
|
});
|
|
6615
|
-
|
|
6616
|
-
test('runs lock-free eventless methods without unlocking or synthesized UI', async () => {
|
|
6617
|
-
const method = {
|
|
6618
|
-
name: 'uploadPortfolio',
|
|
6619
|
-
unlockPolicy: 'none',
|
|
6620
|
-
protocolV2UiMode: 'none',
|
|
6621
|
-
run: jest.fn().mockResolvedValue({ message: 'ok' }),
|
|
6622
|
-
};
|
|
6623
|
-
const device = {
|
|
6624
|
-
features: { unlocked: false },
|
|
6625
|
-
isProtocolV2: () => true,
|
|
6626
|
-
unlockDevice: jest.fn().mockResolvedValue(undefined),
|
|
6627
|
-
};
|
|
6628
|
-
const uiCoordinator = {
|
|
6629
|
-
enterMethodInteraction: jest.fn(),
|
|
6630
|
-
enterUnlockInteraction: jest.fn(),
|
|
6631
|
-
resumeMethodInteraction: jest.fn(),
|
|
6632
|
-
};
|
|
6633
|
-
|
|
6634
|
-
await expect(
|
|
6635
|
-
runMethodWithUnlockRetry(method as any, device as any, uiCoordinator as any)
|
|
6636
|
-
).resolves.toEqual({ message: 'ok' });
|
|
6637
|
-
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
6638
|
-
expect(uiCoordinator.enterMethodInteraction).not.toHaveBeenCalled();
|
|
6639
|
-
expect(uiCoordinator.enterUnlockInteraction).not.toHaveBeenCalled();
|
|
6640
|
-
expect(uiCoordinator.resumeMethodInteraction).not.toHaveBeenCalled();
|
|
6641
|
-
});
|
|
6642
|
-
|
|
6643
|
-
test('keeps the PIN prompt when an eventless method actually unlocks the device', async () => {
|
|
6644
|
-
const lockedError = deviceLockedError();
|
|
6645
|
-
const method = {
|
|
6646
|
-
name: 'backgroundRead',
|
|
6647
|
-
unlockPolicy: 'retry-on-locked',
|
|
6648
|
-
protocolV2UiMode: 'none',
|
|
6649
|
-
run: jest.fn().mockRejectedValueOnce(lockedError).mockResolvedValueOnce({ message: 'ok' }),
|
|
6650
|
-
};
|
|
6651
|
-
const device = {
|
|
6652
|
-
isProtocolV2: () => true,
|
|
6653
|
-
unlockDevice: jest.fn().mockResolvedValue(undefined),
|
|
6654
|
-
};
|
|
6655
|
-
const uiCoordinator = {
|
|
6656
|
-
enterMethodInteraction: jest.fn(),
|
|
6657
|
-
enterUnlockInteraction: jest.fn(),
|
|
6658
|
-
resumeMethodInteraction: jest.fn(),
|
|
6659
|
-
};
|
|
6660
|
-
|
|
6661
|
-
await expect(
|
|
6662
|
-
runMethodWithUnlockRetry(method as any, device as any, uiCoordinator as any)
|
|
6663
|
-
).resolves.toEqual({ message: 'ok' });
|
|
6664
|
-
expect(device.unlockDevice).toHaveBeenCalledWith(undefined, {
|
|
6665
|
-
source: 'unlock-coordinator',
|
|
6666
|
-
reason: 'device-locked',
|
|
6667
|
-
deviceOnly: true,
|
|
6668
|
-
method: 'backgroundRead',
|
|
6669
|
-
});
|
|
6670
|
-
expect(uiCoordinator.enterMethodInteraction).not.toHaveBeenCalled();
|
|
6671
|
-
expect(uiCoordinator.enterUnlockInteraction).not.toHaveBeenCalled();
|
|
6672
|
-
expect(uiCoordinator.resumeMethodInteraction).not.toHaveBeenCalled();
|
|
6673
|
-
});
|
|
6674
|
-
|
|
6675
|
-
test('does not unlock or replay a lock-free state-changing method after a locked response', async () => {
|
|
6676
|
-
const error = deviceLockedError();
|
|
6677
|
-
const method = {
|
|
6678
|
-
name: 'uploadPortfolio',
|
|
6679
|
-
unlockPolicy: 'none',
|
|
6680
|
-
run: jest.fn().mockRejectedValue(error),
|
|
6681
|
-
};
|
|
6682
|
-
const device = {
|
|
6683
|
-
features: { unlocked: true },
|
|
6684
|
-
isProtocolV2: () => true,
|
|
6685
|
-
unlockDevice: jest.fn(),
|
|
6686
|
-
};
|
|
6687
|
-
|
|
6688
|
-
await expect(runMethodWithUnlockRetry(method as any, device as any)).rejects.toBe(error);
|
|
6689
|
-
expect(method.run).toHaveBeenCalledTimes(1);
|
|
6690
|
-
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
6691
|
-
});
|
|
6692
|
-
|
|
6693
|
-
test('does not retry when unlock fails or when the retry is still locked', async () => {
|
|
6694
|
-
const initialError = deviceLockedError();
|
|
6695
|
-
const unlockError = new Error('PIN cancelled');
|
|
6696
|
-
const unlockFailMethod = {
|
|
6697
|
-
unlockPolicy: 'retry-on-locked',
|
|
6698
|
-
run: jest.fn().mockRejectedValue(initialError),
|
|
6699
|
-
};
|
|
6700
|
-
const unlockFailDevice = {
|
|
6701
|
-
isProtocolV2: () => true,
|
|
6702
|
-
unlockDevice: jest.fn().mockRejectedValue(unlockError),
|
|
6703
|
-
};
|
|
6704
|
-
const unlockFailCoordinator = {
|
|
6705
|
-
enterMethodInteraction: jest.fn(),
|
|
6706
|
-
enterUnlockInteraction: jest.fn(),
|
|
6707
|
-
resumeMethodInteraction: jest.fn(),
|
|
6708
|
-
};
|
|
6709
|
-
|
|
6710
|
-
await expect(
|
|
6711
|
-
runMethodWithUnlockRetry(
|
|
6712
|
-
unlockFailMethod as any,
|
|
6713
|
-
unlockFailDevice as any,
|
|
6714
|
-
unlockFailCoordinator as any
|
|
6715
|
-
)
|
|
6716
|
-
).rejects.toBe(unlockError);
|
|
6717
|
-
expect(unlockFailMethod.run).toHaveBeenCalledTimes(1);
|
|
6718
|
-
expect(unlockFailCoordinator.enterUnlockInteraction).toHaveBeenCalledTimes(1);
|
|
6719
|
-
expect(unlockFailCoordinator.resumeMethodInteraction).not.toHaveBeenCalled();
|
|
6720
|
-
|
|
6721
|
-
const retryError = deviceLockedError();
|
|
6722
|
-
const retryFailMethod = {
|
|
6723
|
-
unlockPolicy: 'retry-on-locked',
|
|
6724
|
-
run: jest.fn().mockRejectedValueOnce(initialError).mockRejectedValueOnce(retryError),
|
|
6725
|
-
};
|
|
6726
|
-
const retryFailDevice = {
|
|
6727
|
-
isProtocolV2: () => true,
|
|
6728
|
-
unlockDevice: jest.fn().mockResolvedValue(undefined),
|
|
6729
|
-
};
|
|
6730
|
-
const retryFailCoordinator = {
|
|
6731
|
-
enterMethodInteraction: jest.fn(),
|
|
6732
|
-
enterUnlockInteraction: jest.fn(),
|
|
6733
|
-
resumeMethodInteraction: jest.fn(),
|
|
6734
|
-
};
|
|
6735
|
-
|
|
6736
|
-
await expect(
|
|
6737
|
-
runMethodWithUnlockRetry(
|
|
6738
|
-
retryFailMethod as any,
|
|
6739
|
-
retryFailDevice as any,
|
|
6740
|
-
retryFailCoordinator as any
|
|
6741
|
-
)
|
|
6742
|
-
).rejects.toBe(retryError);
|
|
6743
|
-
expect(retryFailMethod.run).toHaveBeenCalledTimes(2);
|
|
6744
|
-
expect(retryFailDevice.unlockDevice).toHaveBeenCalledTimes(1);
|
|
6745
|
-
expect(retryFailCoordinator.enterUnlockInteraction).toHaveBeenCalledTimes(1);
|
|
6746
|
-
expect(retryFailCoordinator.resumeMethodInteraction).toHaveBeenCalledTimes(1);
|
|
6747
|
-
});
|
|
6748
6472
|
});
|
|
6749
6473
|
|
|
6750
6474
|
describe('Protocol V2 current low-level methods', () => {
|