@onekeyfe/hd-core 1.2.0-alpha.58 → 1.2.0-alpha.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/DeviceCommands.test.ts +5 -15
- package/__tests__/device-pool-state.test.ts +22 -0
- package/__tests__/device-state-mapper.test.ts +12 -0
- package/__tests__/device-utils.test.ts +48 -1
- package/__tests__/deviceSettings.test.ts +10 -0
- package/__tests__/deviceUploadNft.test.ts +4 -28
- package/__tests__/kaspa-use-tweak-pro2.test.ts +2 -2
- package/__tests__/protocol-v2-resources.test.ts +28 -13
- package/__tests__/protocol-v2-unlock-policy.test.ts +34 -0
- package/__tests__/protocol-v2.test.ts +159 -137
- package/__tests__/tron-sign-message-init.test.ts +2 -2
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +4 -3
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/kaspa/KaspaGetAddress.d.ts +1 -1
- package/dist/api/kaspa/KaspaSignTransaction.d.ts +1 -1
- package/dist/api/polkadot/PolkadotGetAddress.d.ts +1 -1
- package/dist/api/polkadot/networks.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/api/solana/SolSignMessage.d.ts +1 -1
- package/dist/api/solana/SolSignOffchainMessage.d.ts +1 -1
- package/dist/api/solana/SolSignTransaction.d.ts +2 -2
- package/dist/api/sui/SuiGetAddress.d.ts +1 -1
- package/dist/api/sui/SuiGetPublicKey.d.ts +1 -1
- package/dist/api/sui/SuiSignMessage.d.ts +1 -1
- package/dist/api/sui/SuiSignTransaction.d.ts +1 -1
- package/dist/api/ton/TonGetAddress.d.ts +1 -1
- package/dist/api/ton/TonSignMessage.d.ts +1 -1
- package/dist/api/ton/TonSignProof.d.ts +1 -1
- package/dist/api/tron/TronSignMessage.d.ts +2 -2
- package/dist/api/tron/TronSignTransaction.d.ts +1 -1
- package/dist/data-manager/DataManager.d.ts +7 -5
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -1
- package/dist/index.d.ts +44 -34
- package/dist/index.js +231 -168
- package/dist/protocols/protocol-v2/resources.d.ts +2 -2
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +3 -2
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/device.d.ts +2 -2
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/settings.d.ts +22 -14
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
- package/dist/utils/deviceSettings.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +7 -5
- package/src/api/FirmwareUpdateV4.ts +115 -102
- package/src/api/OpenWalletSession.ts +1 -0
- package/src/api/kaspa/KaspaGetAddress.ts +1 -1
- package/src/api/kaspa/KaspaSignTransaction.ts +1 -1
- package/src/api/polkadot/networks.ts +3 -3
- package/src/api/protocol-v2/DeviceUploadNft.ts +7 -12
- package/src/api/protocol-v2/helpers.ts +0 -1
- package/src/api/solana/SolSignMessage.ts +1 -1
- package/src/api/solana/SolSignOffchainMessage.ts +1 -1
- package/src/api/solana/SolSignTransaction.ts +2 -2
- package/src/api/sui/SuiGetAddress.ts +1 -1
- package/src/api/sui/SuiGetPublicKey.ts +1 -1
- package/src/api/sui/SuiSignMessage.ts +1 -1
- package/src/api/sui/SuiSignTransaction.ts +1 -1
- package/src/api/ton/TonGetAddress.ts +1 -1
- package/src/api/ton/TonSignMessage.ts +1 -1
- package/src/api/ton/TonSignProof.ts +1 -1
- package/src/api/tron/TronSignMessage.ts +2 -2
- package/src/api/tron/TronSignTransaction.ts +1 -1
- package/src/data-manager/DataManager.ts +28 -9
- package/src/device/Device.ts +15 -6
- package/src/device/DeviceCommands.ts +13 -10
- package/src/device/DevicePool.ts +17 -8
- package/src/deviceProfile/buildDeviceFeatures.ts +2 -2
- package/src/deviceProfile/protocolV2DeviceIdentity.ts +3 -0
- package/src/protocols/protocol-v2/resources.ts +44 -13
- package/src/types/api/checkAllFirmwareRelease.ts +1 -1
- package/src/types/api/firmwareUpdate.ts +5 -5
- package/src/types/device.ts +12 -3
- package/src/types/settings.ts +30 -21
- package/src/utils/deviceFeaturesCompat.ts +5 -0
- package/src/utils/deviceFeaturesUtils.ts +6 -3
- package/src/utils/deviceInfoUtils.ts +2 -0
- package/src/utils/deviceSettings.ts +3 -0
|
@@ -363,14 +363,9 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
363
363
|
}
|
|
364
364
|
);
|
|
365
365
|
|
|
366
|
-
it.each([
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
['SolanaSignTx', 'Localized cancellation message'],
|
|
370
|
-
['GetAddress', 'Address confirmation rejected'],
|
|
371
|
-
] as const)(
|
|
372
|
-
'maps Protocol V2 cancellation subcode 1 for %s regardless of message',
|
|
373
|
-
async (callType, message) => {
|
|
366
|
+
it.each(['SignTx', 'EthereumSignTypedDataOneKey', 'SolanaSignTx', 'GetAddress'] as const)(
|
|
367
|
+
'does not treat domain-ambiguous Protocol V2 subcode 1 for %s as cancellation',
|
|
368
|
+
async callType => {
|
|
374
369
|
const commands = createCommands();
|
|
375
370
|
|
|
376
371
|
await expect(
|
|
@@ -380,18 +375,13 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
380
375
|
message: {
|
|
381
376
|
code: 'Failure_ProcessError',
|
|
382
377
|
subcode: 1,
|
|
383
|
-
message,
|
|
378
|
+
message: 'Domain-specific failure',
|
|
384
379
|
},
|
|
385
380
|
} as any,
|
|
386
381
|
callType
|
|
387
382
|
)
|
|
388
383
|
).rejects.toMatchObject({
|
|
389
|
-
errorCode: HardwareErrorCode.
|
|
390
|
-
params: {
|
|
391
|
-
failureCode: 'Failure_ProcessError',
|
|
392
|
-
subcode: 1,
|
|
393
|
-
firmwareMessage: message,
|
|
394
|
-
},
|
|
384
|
+
errorCode: HardwareErrorCode.RuntimeError,
|
|
395
385
|
});
|
|
396
386
|
}
|
|
397
387
|
);
|
|
@@ -38,6 +38,28 @@ describe('DevicePool state lifecycle', () => {
|
|
|
38
38
|
expect(getDeviceState).toHaveBeenNthCalledWith(2, { refreshSections: ['settings'] });
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
+
test('actively re-detects a cached device when protocol detection is forced', async () => {
|
|
42
|
+
const descriptor = { path: 'cached-path', protocolType: 'V1' } as any;
|
|
43
|
+
const device = Device.fromDescriptor(descriptor);
|
|
44
|
+
const acquire = jest.spyOn(device, 'acquire').mockImplementation(() => {
|
|
45
|
+
device.originalDescriptor.protocolType = 'V2';
|
|
46
|
+
return Promise.resolve(true);
|
|
47
|
+
});
|
|
48
|
+
const initialize = jest.spyOn(device, 'initialize').mockResolvedValue(undefined);
|
|
49
|
+
const release = jest.spyOn(device, 'release').mockResolvedValue(undefined);
|
|
50
|
+
DevicePool.devicesCache = { 'cached-id': device };
|
|
51
|
+
|
|
52
|
+
const result = await DevicePool.getDevices([descriptor], 'cached-id', {
|
|
53
|
+
forceProtocolDetection: true,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(acquire).toHaveBeenCalledWith(undefined, { forceProtocolDetection: true });
|
|
57
|
+
expect(initialize).toHaveBeenCalledWith({ forceProtocolDetection: true });
|
|
58
|
+
expect(release).toHaveBeenCalledTimes(1);
|
|
59
|
+
expect(device.originalDescriptor.protocolType).toBe('V2');
|
|
60
|
+
expect(result.deviceList).toEqual([device]);
|
|
61
|
+
});
|
|
62
|
+
|
|
41
63
|
test('keeps a discovered device when its Protocol V2 label cannot be read', async () => {
|
|
42
64
|
const labelError = new Error('settings unavailable');
|
|
43
65
|
const getDeviceState = jest
|
|
@@ -145,6 +145,18 @@ describe('DeviceStateMapper', () => {
|
|
|
145
145
|
expect(patch.status?.unlocked).toBeUndefined();
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
+
test('maps the Protocol V2 Neo device type without treating it as Pro2', () => {
|
|
149
|
+
const patch = mapProtocolV2DeviceInfoToState({
|
|
150
|
+
hw: { Device_type: DeviceType.NEO, serial_no: 'NEO-SERIAL-1' },
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
expect(patch.identity).toMatchObject({
|
|
154
|
+
deviceType: EDeviceType.Neo,
|
|
155
|
+
model: 'neo',
|
|
156
|
+
serialNo: 'NEO-SERIAL-1',
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
148
160
|
test('maps the hardware model independently from Protocol V2', () => {
|
|
149
161
|
const pro = mapProtocolV2DeviceInfoToState({
|
|
150
162
|
protocol_version: 2,
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { DeviceType } from '@onekeyfe/hd-transport';
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
getDeviceSerialNo,
|
|
5
6
|
getDeviceType,
|
|
7
|
+
getDeviceTypeByBleName,
|
|
6
8
|
getDeviceUUID,
|
|
7
9
|
getFirmwareType,
|
|
10
|
+
getMethodVersionRange,
|
|
8
11
|
} from '../src/utils/deviceInfoUtils';
|
|
9
12
|
import {
|
|
10
13
|
getDeviceBLEFirmwareVersion,
|
|
@@ -85,7 +88,7 @@ describe('device feature selectors', () => {
|
|
|
85
88
|
test('reads normalized Protocol V2 features through the same public selectors', () => {
|
|
86
89
|
const features = buildProtocolV2FeaturesPayload({
|
|
87
90
|
deviceInfo: {
|
|
88
|
-
hw: { serial_no: 'P2-001' },
|
|
91
|
+
hw: { Device_type: DeviceType.PRO2, serial_no: 'P2-001' },
|
|
89
92
|
fw: {
|
|
90
93
|
application: { version: '5.0.0' },
|
|
91
94
|
bootloader: { version: '2.0.0' },
|
|
@@ -104,4 +107,48 @@ describe('device feature selectors', () => {
|
|
|
104
107
|
expect(getDeviceBoardloaderVersion(features)).toEqual([1, 0, 0]);
|
|
105
108
|
expect(getDeviceBLEFirmwareVersion(features)).toEqual([3, 0, 0]);
|
|
106
109
|
});
|
|
110
|
+
|
|
111
|
+
test('preserves the real Neo type in Protocol V2 compatibility features', () => {
|
|
112
|
+
const features = buildProtocolV2FeaturesPayload({
|
|
113
|
+
deviceInfo: {
|
|
114
|
+
hw: { Device_type: DeviceType.NEO, serial_no: 'NEO-001' },
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(features.deviceType).toBe(EDeviceType.Neo);
|
|
119
|
+
expect(features.onekey_device_type).toBe('NEO');
|
|
120
|
+
expect(getDeviceType(features)).toBe(EDeviceType.Neo);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('recognizes Neo discovery names before initialization', () => {
|
|
124
|
+
expect(getDeviceTypeByBleName('OneKey Neo 1234')).toBe(EDeviceType.Neo);
|
|
125
|
+
expect(getDeviceTypeByBleName('Neo 1234')).toBe(EDeviceType.Neo);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test.each([EDeviceType.Pro2, EDeviceType.Neo])(
|
|
129
|
+
'uses the Pro2 product model as the chain capability fallback for %s',
|
|
130
|
+
deviceType => {
|
|
131
|
+
const checkedTypes: string[] = [];
|
|
132
|
+
const versionRange = getMethodVersionRange({ deviceType } as PROTO.Features, type => {
|
|
133
|
+
checkedTypes.push(type);
|
|
134
|
+
return type === 'model_pro2' ? { min: '1.0.0' } : undefined;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(versionRange).toEqual({ min: '1.0.0' });
|
|
138
|
+
expect(checkedTypes).toEqual([deviceType, 'model_pro2']);
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
test('prefers a Neo-specific chain range over the shared Pro2 product model', () => {
|
|
143
|
+
const versionRange = getMethodVersionRange(
|
|
144
|
+
{ deviceType: EDeviceType.Neo } as PROTO.Features,
|
|
145
|
+
type => {
|
|
146
|
+
if (type === EDeviceType.Neo) return { min: '2.0.0' };
|
|
147
|
+
if (type === 'model_pro2') return { min: '1.0.0' };
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
expect(versionRange).toEqual({ min: '2.0.0' });
|
|
153
|
+
});
|
|
107
154
|
});
|
|
@@ -72,6 +72,16 @@ describe('Pro 2 device settings options', () => {
|
|
|
72
72
|
]);
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
+
test('uses the shared Protocol V2 settings model for Neo', () => {
|
|
76
|
+
expect(getLanguageConfig(EDeviceType.Neo)).toEqual(getLanguageConfig(EDeviceType.Pro2));
|
|
77
|
+
expect(getAutoLockOptions(EDeviceType.Neo, 'V2')).toEqual(
|
|
78
|
+
getAutoLockOptions(EDeviceType.Pro2, 'V2')
|
|
79
|
+
);
|
|
80
|
+
expect(getAutoShutDownOptions(EDeviceType.Neo, 'V2')).toEqual(
|
|
81
|
+
getAutoShutDownOptions(EDeviceType.Pro2, 'V2')
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
75
85
|
test('keeps the legacy and Protocol V2 never values protocol-specific', () => {
|
|
76
86
|
expect(getAutoLockOptions(EDeviceType.Pro, 'V1').slice(-1)[0]).toEqual({
|
|
77
87
|
label: 'Never',
|
|
@@ -160,30 +160,24 @@ describe('DeviceUploadNft', () => {
|
|
|
160
160
|
});
|
|
161
161
|
});
|
|
162
162
|
|
|
163
|
-
test('
|
|
163
|
+
test('does not retry a timed-out NftUpdate', async () => {
|
|
164
164
|
const timeout = Object.assign(new Error('Protocol V2 response timeout'), {
|
|
165
165
|
code: 'response-timeout',
|
|
166
166
|
});
|
|
167
|
-
let updateCalls = 0;
|
|
168
167
|
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
169
168
|
if (request === 'FilesystemDirList') {
|
|
170
169
|
return { message: { path: 'vol1:/nft', child_files: '' } };
|
|
171
170
|
}
|
|
172
171
|
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
173
|
-
if (request === 'NftUpdate')
|
|
174
|
-
updateCalls += 1;
|
|
175
|
-
if (updateCalls === 1) throw timeout;
|
|
176
|
-
return { message: { message: 'NFT updated' } };
|
|
177
|
-
}
|
|
172
|
+
if (request === 'NftUpdate') throw timeout;
|
|
178
173
|
throw new Error(`Unexpected request: ${request}`);
|
|
179
174
|
});
|
|
180
175
|
const method = createMethod({ typedCall });
|
|
181
176
|
|
|
182
|
-
await expect(method.run()).
|
|
177
|
+
await expect(method.run()).rejects.toBe(timeout);
|
|
183
178
|
|
|
184
179
|
const updateRequests = typedCall.mock.calls.filter(call => call[0] === 'NftUpdate');
|
|
185
|
-
expect(updateRequests).toHaveLength(
|
|
186
|
-
expect(updateRequests[1]).toEqual(updateRequests[0]);
|
|
180
|
+
expect(updateRequests).toHaveLength(1);
|
|
187
181
|
});
|
|
188
182
|
|
|
189
183
|
test('does not retry a non-timeout NftUpdate failure', async () => {
|
|
@@ -201,24 +195,6 @@ describe('DeviceUploadNft', () => {
|
|
|
201
195
|
expect(typedCall.mock.calls.filter(call => call[0] === 'NftUpdate')).toHaveLength(1);
|
|
202
196
|
});
|
|
203
197
|
|
|
204
|
-
test('propagates a second NftUpdate timeout after one retry', async () => {
|
|
205
|
-
const timeout = Object.assign(new Error('Protocol V2 response timeout'), {
|
|
206
|
-
code: 'response-timeout',
|
|
207
|
-
});
|
|
208
|
-
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
209
|
-
if (request === 'FilesystemDirList') {
|
|
210
|
-
return { message: { path: 'vol1:/nft', child_files: '' } };
|
|
211
|
-
}
|
|
212
|
-
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
213
|
-
if (request === 'NftUpdate') throw timeout;
|
|
214
|
-
throw new Error(`Unexpected request: ${request}`);
|
|
215
|
-
});
|
|
216
|
-
const method = createMethod({ typedCall });
|
|
217
|
-
|
|
218
|
-
await expect(method.run()).rejects.toBe(timeout);
|
|
219
|
-
expect(typedCall.mock.calls.filter(call => call[0] === 'NftUpdate')).toHaveLength(2);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
198
|
test('does not publish when a file write fails', async () => {
|
|
223
199
|
const typedCall = jest.fn((request: string) => {
|
|
224
200
|
if (request === 'FilesystemDirList') {
|
|
@@ -6,7 +6,7 @@ jest.mock('../src/data/config', () => ({
|
|
|
6
6
|
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
7
7
|
}));
|
|
8
8
|
|
|
9
|
-
describe('Kaspa Pro2 useTweak capability', () => {
|
|
9
|
+
describe('Kaspa Pro2 product-family useTweak capability', () => {
|
|
10
10
|
test.each([
|
|
11
11
|
['get address', KaspaGetAddress],
|
|
12
12
|
['sign transaction', KaspaSignTransaction],
|
|
@@ -14,7 +14,7 @@ describe('Kaspa Pro2 useTweak capability', () => {
|
|
|
14
14
|
const method = new Method({ id: 1, payload: {} } as never);
|
|
15
15
|
|
|
16
16
|
expect(method.getUseTweakVersionRange()).toMatchObject({
|
|
17
|
-
|
|
17
|
+
model_pro2: { min: '0.0.0' },
|
|
18
18
|
});
|
|
19
19
|
});
|
|
20
20
|
});
|
|
@@ -56,12 +56,17 @@ const resources: IProtocolV2Resource[] = PROTOCOL_V2_RESOURCE_TYPES.map((type, i
|
|
|
56
56
|
|
|
57
57
|
const bootResources: IProtocolV2BootResources = {
|
|
58
58
|
required: false,
|
|
59
|
-
target: '
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
target: 'RES',
|
|
60
|
+
manifestUrl: 'https://example.com/manifest.json',
|
|
61
|
+
files: [
|
|
62
|
+
{
|
|
63
|
+
name: 'bootloader_crest.bin',
|
|
64
|
+
url: 'https://example.com/assets/bootloader_crest.bin',
|
|
65
|
+
devicePath: 'vol0:/assets/loaders/boot.staging/graphics/bootloader_crest.bin',
|
|
66
|
+
size: 1234,
|
|
67
|
+
fileHash: 'ab'.repeat(32),
|
|
68
|
+
},
|
|
69
|
+
],
|
|
65
70
|
};
|
|
66
71
|
|
|
67
72
|
const createSettings = (configFetcher: ConnectSettings['configFetcher']): ConnectSettings =>
|
|
@@ -119,7 +124,7 @@ describe('Pro2 resource configuration', () => {
|
|
|
119
124
|
).toThrow('headerHash');
|
|
120
125
|
});
|
|
121
126
|
|
|
122
|
-
test('requires boot resources to remain optional and use
|
|
127
|
+
test('requires boot resources to remain optional and use RES file entries', () => {
|
|
123
128
|
expect(() =>
|
|
124
129
|
parseProtocolV2Resources({
|
|
125
130
|
stable: resources,
|
|
@@ -129,9 +134,18 @@ describe('Pro2 resource configuration', () => {
|
|
|
129
134
|
expect(() =>
|
|
130
135
|
parseProtocolV2Resources({
|
|
131
136
|
stable: resources,
|
|
132
|
-
boot: { ...bootResources, target: '
|
|
137
|
+
boot: { ...bootResources, target: 'INVALID' },
|
|
133
138
|
})
|
|
134
|
-
).toThrow('expected
|
|
139
|
+
).toThrow('expected RES');
|
|
140
|
+
expect(() =>
|
|
141
|
+
parseProtocolV2Resources({
|
|
142
|
+
stable: resources,
|
|
143
|
+
boot: {
|
|
144
|
+
...bootResources,
|
|
145
|
+
files: [{ ...bootResources.files[0], devicePath: '../outside.bin' }],
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
).toThrow('devicePath');
|
|
135
149
|
});
|
|
136
150
|
|
|
137
151
|
test('downloads nothing when all resource identities match', () => {
|
|
@@ -280,17 +294,18 @@ describe('Pro2 resource configuration', () => {
|
|
|
280
294
|
expect(DataManager.getProtocolV2BootResources()).toBeUndefined();
|
|
281
295
|
});
|
|
282
296
|
|
|
283
|
-
test('
|
|
297
|
+
test('only blocks resource mutation when the refreshed Pro2 resources are invalid', async () => {
|
|
284
298
|
const remoteConfig = createRemoteConfig();
|
|
285
299
|
(remoteConfig.pro2 as { resources?: unknown }).resources = { stable: [] };
|
|
286
300
|
const settings = createSettings(jest.fn().mockResolvedValue(remoteConfig));
|
|
287
301
|
DataManager.settings = settings;
|
|
288
302
|
|
|
289
|
-
await expect(DataManager.forceReloadData()).
|
|
290
|
-
|
|
303
|
+
await expect(DataManager.forceReloadData()).resolves.toBeUndefined();
|
|
304
|
+
await expect(DataManager.forceReloadData({ requireResources: true })).rejects.toMatchObject({
|
|
305
|
+
errorCode: HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
291
306
|
message: expect.stringContaining('Invalid Pro2 resources config'),
|
|
292
307
|
});
|
|
293
|
-
expect(DataManager.lastCheckTimestamp).
|
|
308
|
+
expect(DataManager.lastCheckTimestamp).toBeGreaterThan(0);
|
|
294
309
|
});
|
|
295
310
|
|
|
296
311
|
test('does not advance the cache timestamp when refresh fails', async () => {
|
|
@@ -2,6 +2,7 @@ import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
|
|
|
2
2
|
|
|
3
3
|
import ConfluxSignMessageCIP23 from '../src/api/conflux/ConfluxSignMessageCIP23';
|
|
4
4
|
import DeviceLock from '../src/api/device/DeviceLock';
|
|
5
|
+
import OpenWalletSession from '../src/api/OpenWalletSession';
|
|
5
6
|
import { runMethodWithUnlockPolicy } from '../src/protocols/protocol-v2/unlockPolicyRunner';
|
|
6
7
|
|
|
7
8
|
jest.mock('../src/data/config', () => ({
|
|
@@ -33,6 +34,39 @@ describe('Protocol V2 unlock semantics', () => {
|
|
|
33
34
|
expect(method.unlockPolicy).toBe('none');
|
|
34
35
|
});
|
|
35
36
|
|
|
37
|
+
test('pre-unlocks openWalletSession before opening or restoring a wallet session', async () => {
|
|
38
|
+
const method = new OpenWalletSession({
|
|
39
|
+
id: 1,
|
|
40
|
+
payload: { method: 'openWalletSession', mode: 'standard' },
|
|
41
|
+
});
|
|
42
|
+
method.init();
|
|
43
|
+
const run = jest.fn().mockResolvedValue({ walletType: 'standard' });
|
|
44
|
+
method.run = run as any;
|
|
45
|
+
const features = { unlocked: false };
|
|
46
|
+
const device = {
|
|
47
|
+
features,
|
|
48
|
+
commands: {
|
|
49
|
+
typedCall: jest.fn().mockResolvedValue({ message: { unlocked: false } }),
|
|
50
|
+
},
|
|
51
|
+
isProtocolV2: () => true,
|
|
52
|
+
isBootloader: () => false,
|
|
53
|
+
isRomloader: () => false,
|
|
54
|
+
updateProtocolV2Status: jest.fn(() => features),
|
|
55
|
+
unlockDevice: jest.fn().mockImplementation(() => {
|
|
56
|
+
features.unlocked = true;
|
|
57
|
+
return Promise.resolve(features);
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
await expect(runMethodWithUnlockPolicy(method, device as any)).resolves.toEqual({
|
|
62
|
+
walletType: 'standard',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(method.unlockPolicy).toBe('unlock-before-run');
|
|
66
|
+
expect(device.unlockDevice).toHaveBeenCalledTimes(1);
|
|
67
|
+
expect(run).toHaveBeenCalledTimes(1);
|
|
68
|
+
});
|
|
69
|
+
|
|
36
70
|
test('pre-unlocks an unregistered wallet business method', async () => {
|
|
37
71
|
const features = { unlocked: false };
|
|
38
72
|
const method = {
|