@onekeyfe/hd-core 1.2.2-alpha.100 → 1.2.2-alpha.3
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 +20 -4
- package/__tests__/DeviceCommands.test.ts +13 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -2
- package/__tests__/check-firmware-release-protocol-v2.test.ts +0 -2
- package/__tests__/device-lifecycle-events.test.ts +19 -0
- package/__tests__/deviceUploadNft.test.ts +68 -0
- package/__tests__/evmSignTypedData.test.ts +42 -0
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +825 -12
- package/__tests__/open-wallet-session.test.ts +144 -37
- package/__tests__/pro2HostAssetPackage.test.ts +58 -0
- package/__tests__/protocol-v2-resources.test.ts +7 -4
- package/__tests__/protocol-v2-unlock-policy.test.ts +35 -1
- package/__tests__/protocol-v2.test.ts +720 -51
- package/dist/api/FirmwareUpdateV4.d.ts +4 -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/evm/EVMSignTypedData.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +2 -2
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +1 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +1 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +3 -0
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +21 -5
- package/dist/index.js +2288 -1439
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +2 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +3 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/settings.d.ts +2 -2
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +3 -0
- package/dist/utils/deviceFeaturesUtils.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 +8 -0
- package/dist/utils/pro2HostAssetPackage.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +434 -75
- package/src/api/OpenWalletSession.ts +39 -9
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +5 -2
- package/src/api/evm/EVMSignTypedData.ts +1 -0
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +9 -3
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +7 -1
- package/src/api/protocol-v2/DeviceUploadNft.ts +31 -8
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +27 -8
- package/src/api/protocol-v2/helpers.ts +8 -0
- package/src/core/index.ts +130 -14
- package/src/data/messages/messages-protocol-v2.json +1541 -1332
- package/src/device/Device.ts +3 -1
- package/src/device/DeviceConnector.ts +7 -3
- package/src/events/ui-request.ts +3 -0
- package/src/protocols/protocol-v2/resources.ts +9 -1
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +8 -1
- package/src/protocols/protocol-v2/walletSession.ts +88 -50
- package/src/types/settings.ts +4 -2
- package/src/utils/deviceFeaturesUtils.ts +4 -0
- package/src/utils/pro2HostAssetPackage.ts +354 -0
|
@@ -142,13 +142,12 @@ describe('openWalletSession', () => {
|
|
|
142
142
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
143
143
|
passphrase: '',
|
|
144
144
|
on_device: false,
|
|
145
|
-
});
|
|
146
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
147
145
|
seed_domains: [],
|
|
148
146
|
});
|
|
147
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
149
148
|
});
|
|
150
149
|
|
|
151
|
-
test('
|
|
150
|
+
test('selects the Main PIN before opening the standard wallet when passphrase is disabled', async () => {
|
|
152
151
|
const typedCall = jest.fn((request: string) => {
|
|
153
152
|
if (request === 'ProtocolInfoRequest') {
|
|
154
153
|
return { message: { version: 2 } };
|
|
@@ -167,14 +166,43 @@ describe('openWalletSession', () => {
|
|
|
167
166
|
|
|
168
167
|
await getProtocolV2WalletSession(device as any, { onlyMainPin: true });
|
|
169
168
|
|
|
169
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
|
|
170
|
+
source: 'wallet-session-coordinator',
|
|
171
|
+
reason: 'open-wallet',
|
|
172
|
+
deviceOnly: true,
|
|
173
|
+
});
|
|
170
174
|
expect(typedCall).not.toHaveBeenCalledWith(
|
|
171
175
|
'DeviceSessionAskPassphrase',
|
|
172
176
|
'Success',
|
|
173
177
|
expect.anything()
|
|
174
178
|
);
|
|
175
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
176
|
-
|
|
179
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('reuses a Main PIN selected by the current preflight when passphrase is disabled', async () => {
|
|
183
|
+
const typedCall = jest.fn((request: string) => {
|
|
184
|
+
if (request === 'ProtocolInfoRequest') {
|
|
185
|
+
return { message: { version: 2 } };
|
|
186
|
+
}
|
|
187
|
+
if (request === 'DeviceSessionGet') {
|
|
188
|
+
return {
|
|
189
|
+
message: {
|
|
190
|
+
btc_test_address: 'standard-state',
|
|
191
|
+
session_id: 'standard-session',
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
196
|
+
});
|
|
197
|
+
const device = createDevice({ passphraseProtection: false, typedCall });
|
|
198
|
+
|
|
199
|
+
await getProtocolV2WalletSession(device as any, {
|
|
200
|
+
onlyMainPin: true,
|
|
201
|
+
mainPinSelected: true,
|
|
177
202
|
});
|
|
203
|
+
|
|
204
|
+
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
205
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
178
206
|
});
|
|
179
207
|
|
|
180
208
|
test('does not treat an Attach PIN DeviceStatus as the Main wallet', async () => {
|
|
@@ -301,10 +329,9 @@ describe('openWalletSession', () => {
|
|
|
301
329
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
302
330
|
passphrase: 'host hidden wallet',
|
|
303
331
|
on_device: false,
|
|
304
|
-
});
|
|
305
|
-
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
|
|
306
332
|
seed_domains: [],
|
|
307
333
|
});
|
|
334
|
+
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
|
|
308
335
|
});
|
|
309
336
|
|
|
310
337
|
test('keeps Legacy Protocol V1 getPassphraseState parameterless', async () => {
|
|
@@ -421,10 +448,9 @@ describe('openWalletSession', () => {
|
|
|
421
448
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
422
449
|
passphrase: 'host hidden wallet',
|
|
423
450
|
on_device: false,
|
|
424
|
-
});
|
|
425
|
-
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
|
|
426
451
|
seed_domains: [],
|
|
427
452
|
});
|
|
453
|
+
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
|
|
428
454
|
});
|
|
429
455
|
|
|
430
456
|
test('refreshes Pro2 status and unlocks before selecting a hidden wallet when locked', async () => {
|
|
@@ -698,9 +724,7 @@ describe('openWalletSession', () => {
|
|
|
698
724
|
});
|
|
699
725
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
700
726
|
expect(device.unlockDevice).not.toHaveBeenCalled();
|
|
701
|
-
expect(device.commands.typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
702
|
-
seed_domains: [],
|
|
703
|
-
});
|
|
727
|
+
expect(device.commands.typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
704
728
|
expect(device.commands.typedCall).not.toHaveBeenCalledWith(
|
|
705
729
|
'DeviceSessionAskPassphrase',
|
|
706
730
|
'Success',
|
|
@@ -796,10 +820,9 @@ describe('openWalletSession', () => {
|
|
|
796
820
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
797
821
|
passphrase: 'host hidden wallet',
|
|
798
822
|
on_device: false,
|
|
799
|
-
});
|
|
800
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
801
823
|
seed_domains: [],
|
|
802
824
|
});
|
|
825
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
803
826
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
804
827
|
expect(deviceWalletSessionStore.get('device-1', 'new-hidden-state')).toBe('new-hidden-session');
|
|
805
828
|
});
|
|
@@ -871,7 +894,6 @@ describe('openWalletSession', () => {
|
|
|
871
894
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
872
895
|
session_id: 'known-session',
|
|
873
896
|
btc_test_address: 'hidden-state',
|
|
874
|
-
seed_domains: [],
|
|
875
897
|
});
|
|
876
898
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
877
899
|
});
|
|
@@ -1054,10 +1076,9 @@ describe('openWalletSession', () => {
|
|
|
1054
1076
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
1055
1077
|
passphrase: '',
|
|
1056
1078
|
on_device: false,
|
|
1057
|
-
});
|
|
1058
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1059
1079
|
seed_domains: [],
|
|
1060
1080
|
});
|
|
1081
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
1061
1082
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1062
1083
|
expect(device.passphraseState).toBeUndefined();
|
|
1063
1084
|
});
|
|
@@ -1114,6 +1135,68 @@ describe('openWalletSession', () => {
|
|
|
1114
1135
|
expect(device.getDeviceState).toHaveBeenCalledTimes(2);
|
|
1115
1136
|
});
|
|
1116
1137
|
|
|
1138
|
+
test('does not restrict a locked hidden-wallet selection to the Main PIN', async () => {
|
|
1139
|
+
const typedCall = jest
|
|
1140
|
+
.fn()
|
|
1141
|
+
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1142
|
+
.mockResolvedValueOnce({ message: {} })
|
|
1143
|
+
.mockResolvedValueOnce({
|
|
1144
|
+
message: {
|
|
1145
|
+
btc_test_address: 'hidden-state',
|
|
1146
|
+
session_id: 'hidden-session',
|
|
1147
|
+
},
|
|
1148
|
+
});
|
|
1149
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
1150
|
+
const method = new OpenWalletSession({
|
|
1151
|
+
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
1152
|
+
});
|
|
1153
|
+
method.init();
|
|
1154
|
+
const device = createDevice({ typedCall, promptPassphrase });
|
|
1155
|
+
device.features.unlocked = false;
|
|
1156
|
+
device.getDeviceState = jest
|
|
1157
|
+
.fn()
|
|
1158
|
+
.mockResolvedValueOnce({
|
|
1159
|
+
identity: { deviceId: undefined },
|
|
1160
|
+
status: { unlocked: false, passphraseProtection: true },
|
|
1161
|
+
})
|
|
1162
|
+
.mockResolvedValueOnce({
|
|
1163
|
+
identity: { deviceId: 'device-1' },
|
|
1164
|
+
status: {
|
|
1165
|
+
unlocked: true,
|
|
1166
|
+
passphraseProtection: true,
|
|
1167
|
+
unlockedAttachPin: false,
|
|
1168
|
+
},
|
|
1169
|
+
})
|
|
1170
|
+
.mockResolvedValueOnce({
|
|
1171
|
+
identity: { deviceId: 'device-1' },
|
|
1172
|
+
status: {
|
|
1173
|
+
unlocked: true,
|
|
1174
|
+
passphraseProtection: true,
|
|
1175
|
+
unlockedAttachPin: false,
|
|
1176
|
+
},
|
|
1177
|
+
});
|
|
1178
|
+
device.unlockDevice = jest.fn().mockImplementation(() => {
|
|
1179
|
+
device.features.unlocked = true;
|
|
1180
|
+
return Promise.resolve(device.features);
|
|
1181
|
+
});
|
|
1182
|
+
method.device = device as any;
|
|
1183
|
+
|
|
1184
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
1185
|
+
walletType: 'hidden',
|
|
1186
|
+
passphraseState: 'hidden-state',
|
|
1187
|
+
});
|
|
1188
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Any, {
|
|
1189
|
+
source: 'unlock-coordinator',
|
|
1190
|
+
reason: 'device-locked',
|
|
1191
|
+
deviceOnly: true,
|
|
1192
|
+
method: 'openWalletSession',
|
|
1193
|
+
});
|
|
1194
|
+
expect(device.unlockDevice).not.toHaveBeenCalledWith(
|
|
1195
|
+
DeviceSessionPinType.Main,
|
|
1196
|
+
expect.anything()
|
|
1197
|
+
);
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1117
1200
|
test('switches from Attach PIN to Main PIN before opening the standard wallet', async () => {
|
|
1118
1201
|
const typedCall = jest
|
|
1119
1202
|
.fn()
|
|
@@ -1164,9 +1247,7 @@ describe('openWalletSession', () => {
|
|
|
1164
1247
|
reason: 'open-wallet',
|
|
1165
1248
|
deviceOnly: true,
|
|
1166
1249
|
});
|
|
1167
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1168
|
-
seed_domains: [],
|
|
1169
|
-
});
|
|
1250
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
1170
1251
|
});
|
|
1171
1252
|
|
|
1172
1253
|
test('selects a hidden wallet without exposing the internal device session', async () => {
|
|
@@ -1199,15 +1280,45 @@ describe('openWalletSession', () => {
|
|
|
1199
1280
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
1200
1281
|
passphrase: 'host hidden wallet',
|
|
1201
1282
|
on_device: false,
|
|
1202
|
-
});
|
|
1203
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1204
1283
|
seed_domains: [],
|
|
1205
1284
|
});
|
|
1285
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
1206
1286
|
expect(promptPassphrase).toHaveBeenCalled();
|
|
1207
1287
|
expect(device.passphraseState).toBeUndefined();
|
|
1208
1288
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('hidden-session');
|
|
1209
1289
|
});
|
|
1210
1290
|
|
|
1291
|
+
test('reuses the fresh unlock and wallet-selection statuses', async () => {
|
|
1292
|
+
const typedCall = jest
|
|
1293
|
+
.fn()
|
|
1294
|
+
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1295
|
+
.mockResolvedValueOnce({ message: {} })
|
|
1296
|
+
.mockResolvedValueOnce({
|
|
1297
|
+
message: {
|
|
1298
|
+
btc_test_address: 'hidden-state',
|
|
1299
|
+
session_id: 'hidden-session',
|
|
1300
|
+
},
|
|
1301
|
+
});
|
|
1302
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
1303
|
+
const method = new OpenWalletSession({
|
|
1304
|
+
payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
|
|
1305
|
+
});
|
|
1306
|
+
method.init();
|
|
1307
|
+
method.protocolV2UnlockContext = {
|
|
1308
|
+
preflightCompleted: true,
|
|
1309
|
+
preflightStatusRefreshed: true,
|
|
1310
|
+
};
|
|
1311
|
+
const device = createDevice({ typedCall, promptPassphrase });
|
|
1312
|
+
method.device = device as any;
|
|
1313
|
+
|
|
1314
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
1315
|
+
walletType: 'hidden',
|
|
1316
|
+
passphraseState: 'hidden-state',
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
expect(device.getDeviceState.mock.calls).toEqual([[], []]);
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1211
1322
|
test('gets the prepared session after host passphrase entry', async () => {
|
|
1212
1323
|
const typedCall = jest
|
|
1213
1324
|
.fn()
|
|
@@ -1230,10 +1341,9 @@ describe('openWalletSession', () => {
|
|
|
1230
1341
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
1231
1342
|
passphrase: 'host hidden wallet',
|
|
1232
1343
|
on_device: false,
|
|
1233
|
-
});
|
|
1234
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1235
1344
|
seed_domains: [],
|
|
1236
1345
|
});
|
|
1346
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
1237
1347
|
});
|
|
1238
1348
|
|
|
1239
1349
|
test('selects an on-device passphrase wallet with an explicit on_device request', async () => {
|
|
@@ -1278,10 +1388,9 @@ describe('openWalletSession', () => {
|
|
|
1278
1388
|
});
|
|
1279
1389
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
1280
1390
|
on_device: true,
|
|
1281
|
-
});
|
|
1282
|
-
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
|
|
1283
1391
|
seed_domains: [],
|
|
1284
1392
|
});
|
|
1393
|
+
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
|
|
1285
1394
|
expect(device.createProtocolV2UiPhaseMetadata).toHaveBeenNthCalledWith(
|
|
1286
1395
|
2,
|
|
1287
1396
|
'passphrase-on-device',
|
|
@@ -1332,10 +1441,9 @@ describe('openWalletSession', () => {
|
|
|
1332
1441
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
1333
1442
|
passphrase: 'host hidden wallet',
|
|
1334
1443
|
on_device: false,
|
|
1335
|
-
});
|
|
1336
|
-
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
|
|
1337
1444
|
seed_domains: [],
|
|
1338
1445
|
});
|
|
1446
|
+
expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
|
|
1339
1447
|
});
|
|
1340
1448
|
|
|
1341
1449
|
test('normalizes a Unicode Host passphrase before sending it to Pro2 firmware', async () => {
|
|
@@ -1357,6 +1465,7 @@ describe('openWalletSession', () => {
|
|
|
1357
1465
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
1358
1466
|
passphrase: 'cafe\u0301',
|
|
1359
1467
|
on_device: false,
|
|
1468
|
+
seed_domains: [],
|
|
1360
1469
|
});
|
|
1361
1470
|
});
|
|
1362
1471
|
|
|
@@ -1380,6 +1489,7 @@ describe('openWalletSession', () => {
|
|
|
1380
1489
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
|
|
1381
1490
|
passphrase,
|
|
1382
1491
|
on_device: false,
|
|
1492
|
+
seed_domains: [],
|
|
1383
1493
|
});
|
|
1384
1494
|
});
|
|
1385
1495
|
|
|
@@ -1415,9 +1525,7 @@ describe('openWalletSession', () => {
|
|
|
1415
1525
|
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.AttachToPin, {
|
|
1416
1526
|
emitUiEvent: false,
|
|
1417
1527
|
});
|
|
1418
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1419
|
-
seed_domains: [],
|
|
1420
|
-
});
|
|
1528
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
1421
1529
|
});
|
|
1422
1530
|
|
|
1423
1531
|
test('uses the complete Attach PIN wire flow without a main PIN unlock', async () => {
|
|
@@ -1446,7 +1554,7 @@ describe('openWalletSession', () => {
|
|
|
1446
1554
|
['ProtocolInfoRequest', 'ProtocolInfo', { eventless_wallet_session: true }],
|
|
1447
1555
|
['DeviceSessionAskPin', 'Success', { type: DeviceSessionPinType.AttachToPin }],
|
|
1448
1556
|
['DeviceStatusGet', 'DeviceStatus', {}],
|
|
1449
|
-
['DeviceSessionGet', 'DeviceSession', {
|
|
1557
|
+
['DeviceSessionGet', 'DeviceSession', {}],
|
|
1450
1558
|
]);
|
|
1451
1559
|
expect(device.commands.typedCall).not.toHaveBeenCalledWith('DeviceSessionAskPin', 'Success', {
|
|
1452
1560
|
type: DeviceSessionPinType.Main,
|
|
@@ -1658,7 +1766,6 @@ describe('openWalletSession', () => {
|
|
|
1658
1766
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1659
1767
|
session_id: 'known-session',
|
|
1660
1768
|
btc_test_address: 'hidden-state',
|
|
1661
|
-
seed_domains: [],
|
|
1662
1769
|
});
|
|
1663
1770
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1664
1771
|
});
|
|
@@ -1718,7 +1825,6 @@ describe('openWalletSession', () => {
|
|
|
1718
1825
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1719
1826
|
session_id: 'known-session',
|
|
1720
1827
|
btc_test_address: 'hidden-state',
|
|
1721
|
-
seed_domains: [],
|
|
1722
1828
|
});
|
|
1723
1829
|
const sessionGetCall = typedCall.mock.calls.findIndex(
|
|
1724
1830
|
([requestName]) => requestName === 'DeviceSessionGet'
|
|
@@ -1816,7 +1922,6 @@ describe('openWalletSession', () => {
|
|
|
1816
1922
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1817
1923
|
session_id: 'expired-session',
|
|
1818
1924
|
btc_test_address: 'hidden-state',
|
|
1819
|
-
seed_domains: [],
|
|
1820
1925
|
});
|
|
1821
1926
|
expect(promptPassphrase).toHaveBeenCalledTimes(1);
|
|
1822
1927
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('renewed-session');
|
|
@@ -1854,7 +1959,6 @@ describe('openWalletSession', () => {
|
|
|
1854
1959
|
});
|
|
1855
1960
|
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1856
1961
|
btc_test_address: 'hidden-state',
|
|
1857
|
-
seed_domains: [],
|
|
1858
1962
|
});
|
|
1859
1963
|
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1860
1964
|
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('new-session');
|
|
@@ -1889,11 +1993,14 @@ describe('openWalletSession', () => {
|
|
|
1889
1993
|
walletType: 'hidden',
|
|
1890
1994
|
passphraseState: 'hidden-state',
|
|
1891
1995
|
});
|
|
1892
|
-
expect(typedCall).toHaveBeenCalledWith('
|
|
1996
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
|
|
1997
|
+
passphrase: 'host hidden wallet',
|
|
1998
|
+
on_device: false,
|
|
1893
1999
|
seed_domains: [
|
|
1894
2000
|
DeviceSessionSeedDomain.SeedDomain_Standard,
|
|
1895
2001
|
...(deriveCardano ? [DeviceSessionSeedDomain.SeedDomain_Cardano] : []),
|
|
1896
2002
|
],
|
|
1897
2003
|
});
|
|
2004
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
1898
2005
|
});
|
|
1899
2006
|
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { sha3_512 } from '@noble/hashes/sha3';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
buildPro2HostAssetPackage,
|
|
5
|
+
supportsPro2HostAssetPackage,
|
|
6
|
+
} from '../src/utils/pro2HostAssetPackage';
|
|
7
|
+
|
|
8
|
+
describe('Pro2 host asset package', () => {
|
|
9
|
+
test('builds the unsigned RESOURCE container and LZ4-blocked archive expected by firmware', () => {
|
|
10
|
+
const raw = new TextEncoder().encode('123456789');
|
|
11
|
+
const packageData = buildPro2HostAssetPackage([{ name: 'wallpaper.bin', data: raw }]);
|
|
12
|
+
const headerSize = 0x5f90;
|
|
13
|
+
const payload = packageData.subarray(headerSize);
|
|
14
|
+
const packageView = new DataView(
|
|
15
|
+
packageData.buffer,
|
|
16
|
+
packageData.byteOffset,
|
|
17
|
+
packageData.byteLength
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
expect(packageView.getUint32(0, true)).toBe(0x50504b4f);
|
|
21
|
+
expect(packageView.getUint32(4, true)).toBe(1);
|
|
22
|
+
expect(packageView.getUint32(8, true)).toBe(0x43534552);
|
|
23
|
+
expect(packageView.getUint32(0x0c, true)).toBe(headerSize);
|
|
24
|
+
expect(packageView.getUint32(0x10, true)).toBe(1);
|
|
25
|
+
expect(packageView.getUint32(0x14, true)).toBe(payload.byteLength);
|
|
26
|
+
expect(packageData.subarray(0x200, 0x240)).toEqual(sha3_512(payload));
|
|
27
|
+
expect(packageData.subarray(0x240, 0x280)).toEqual(sha3_512(packageData.subarray(0, 0x240)));
|
|
28
|
+
expect(packageData[0x400]).toBe(0);
|
|
29
|
+
expect(packageView.getUint32(0x408, true)).toBe(0x71717171);
|
|
30
|
+
|
|
31
|
+
const archiveView = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
|
|
32
|
+
expect(archiveView.getUint32(0, true)).toBe(0x52414b4f);
|
|
33
|
+
expect(archiveView.getUint32(4, true)).toBe(1);
|
|
34
|
+
expect(archiveView.getUint16(8, true)).toBe(1);
|
|
35
|
+
expect(new TextDecoder().decode(payload.subarray(43, 56))).toBe('wallpaper.bin');
|
|
36
|
+
expect(archiveView.getUint32(42 + 0x100, true)).toBe(340);
|
|
37
|
+
expect(archiveView.getUint32(42 + 0x104, true)).toBe(raw.byteLength);
|
|
38
|
+
expect(archiveView.getUint32(42 + 0x10c, true)).toBe(0xcbf43926);
|
|
39
|
+
expect(payload[42 + 0x114]).toBe(1);
|
|
40
|
+
|
|
41
|
+
const compressedOffset = archiveView.getUint32(42 + 0x100, true);
|
|
42
|
+
expect(archiveView.getUint16(compressedOffset, true)).toBe(1);
|
|
43
|
+
expect(archiveView.getUint16(compressedOffset + 2, true)).toBe(12);
|
|
44
|
+
expect(archiveView.getUint32(compressedOffset + 4, true)).toBe(0);
|
|
45
|
+
expect(archiveView.getUint32(compressedOffset + 8, true)).toBe(10);
|
|
46
|
+
expect(payload.subarray(compressedOffset + 12)).toEqual(new Uint8Array([0x90, ...raw]));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test.each([
|
|
50
|
+
['1.0.0', false],
|
|
51
|
+
['1.0.1-beta.1', false],
|
|
52
|
+
['1.0.1', true],
|
|
53
|
+
['1.1.0', true],
|
|
54
|
+
[undefined, false],
|
|
55
|
+
])('selects package uploads for firmware %s', (firmwareVersion, expected) => {
|
|
56
|
+
expect(supportsPro2HostAssetPackage(firmwareVersion)).toBe(expected);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -66,8 +66,6 @@ const createRemoteConfig = (): RemoteConfigResponse =>
|
|
|
66
66
|
{
|
|
67
67
|
required: false,
|
|
68
68
|
version: [1, 0, 0],
|
|
69
|
-
url: 'https://example.com/pro2.okpkg',
|
|
70
|
-
fingerprint: 'a'.repeat(64),
|
|
71
69
|
changelog: { 'zh-CN': 'Pro2', 'en-US': 'Pro2' },
|
|
72
70
|
resources: { source: resourceSource },
|
|
73
71
|
},
|
|
@@ -79,8 +77,6 @@ const createRemoteConfig = (): RemoteConfigResponse =>
|
|
|
79
77
|
{
|
|
80
78
|
required: false,
|
|
81
79
|
version: [1, 0, 0],
|
|
82
|
-
url: 'https://example.com/neo.okpkg',
|
|
83
|
-
fingerprint: 'b'.repeat(64),
|
|
84
80
|
changelog: { 'zh-CN': 'Neo', 'en-US': 'Neo' },
|
|
85
81
|
resources: { source: neoResourceSource },
|
|
86
82
|
},
|
|
@@ -109,6 +105,11 @@ describe('Pro2 resource configuration', () => {
|
|
|
109
105
|
expect(
|
|
110
106
|
isProtocolV2ResourceArchiveEntryName('bundles/firmware_logo-pro2-prod_resource-signed.okpkg')
|
|
111
107
|
).toBe(true);
|
|
108
|
+
expect(
|
|
109
|
+
isProtocolV2ResourceArchiveEntryName(
|
|
110
|
+
'pro2-prod_resource/bundles/firmware_logo-pro2-prod_resource-signed.okpkg'
|
|
111
|
+
)
|
|
112
|
+
).toBe(true);
|
|
112
113
|
expect(
|
|
113
114
|
isProtocolV2ResourceArchiveEntryName(
|
|
114
115
|
'__MACOSX/pro2-prod_resource 2/bundles/._firmware_logo-pro2-prod_resource-signed.okpkg'
|
|
@@ -119,6 +120,8 @@ describe('Pro2 resource configuration', () => {
|
|
|
119
120
|
'bundles/._firmware_logo-pro2-prod_resource-signed.okpkg'
|
|
120
121
|
)
|
|
121
122
|
).toBe(false);
|
|
123
|
+
expect(isProtocolV2ResourceArchiveEntryName('pro2-prod_resource/manifest.json')).toBe(false);
|
|
124
|
+
expect(isProtocolV2ResourceArchiveEntryName('pro2-prod_resource/.DS_Store')).toBe(false);
|
|
122
125
|
});
|
|
123
126
|
|
|
124
127
|
test('reads the version, hashes, and direct device path from a RESC package', () => {
|
|
@@ -151,8 +151,42 @@ describe('Protocol V2 unlock semantics', () => {
|
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
expect(method.unlockPolicy).toBe('unlock-before-run');
|
|
154
|
-
expect(device.unlockDevice).
|
|
154
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, expect.any(Object));
|
|
155
155
|
expect(run).toHaveBeenCalledTimes(1);
|
|
156
|
+
expect(method.protocolV2UnlockContext).toMatchObject({
|
|
157
|
+
preflightCompleted: true,
|
|
158
|
+
preflightStatusRefreshed: true,
|
|
159
|
+
preflightMainPinSelected: true,
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('allows either PIN type when pre-unlocking hidden-wallet selection', async () => {
|
|
164
|
+
const method = new OpenWalletSession({
|
|
165
|
+
id: 1,
|
|
166
|
+
payload: { method: 'openWalletSession', mode: 'select-hidden' },
|
|
167
|
+
});
|
|
168
|
+
method.init();
|
|
169
|
+
method.run = jest.fn().mockResolvedValue({ walletType: 'hidden' }) as any;
|
|
170
|
+
const features = { unlocked: false };
|
|
171
|
+
const device = {
|
|
172
|
+
features,
|
|
173
|
+
commands: {
|
|
174
|
+
typedCall: jest.fn().mockResolvedValue({ message: { unlocked: false } }),
|
|
175
|
+
},
|
|
176
|
+
isProtocolV2: () => true,
|
|
177
|
+
isBootloader: () => false,
|
|
178
|
+
isRomloader: () => false,
|
|
179
|
+
updateProtocolV2Status: jest.fn(() => features),
|
|
180
|
+
unlockDevice: jest.fn().mockImplementation(() => {
|
|
181
|
+
features.unlocked = true;
|
|
182
|
+
return Promise.resolve(features);
|
|
183
|
+
}),
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
await runMethodWithUnlockPolicy(method, device as any);
|
|
187
|
+
|
|
188
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Any, expect.any(Object));
|
|
189
|
+
expect(method.protocolV2UnlockContext?.preflightMainPinSelected).toBe(false);
|
|
156
190
|
});
|
|
157
191
|
|
|
158
192
|
test('pre-unlocks an unregistered wallet business method', async () => {
|