@onekeyfe/hd-core 1.2.2-alpha.8 → 1.2.3-alpha.1
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 +254 -1
- package/__tests__/device-lifecycle-events.test.ts +83 -14
- package/__tests__/logBlockEvent.test.ts +45 -122
- package/__tests__/open-wallet-session.test.ts +139 -5
- package/__tests__/protocol-v2.test.ts +81 -2
- package/__tests__/sol-sign-offchain-message.test.ts +72 -0
- package/dist/api/FirmwareUpdateV4.d.ts +1 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts +1 -0
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/solana/SolSignOffchainMessage.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts +2 -0
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/uiPromiseRegistry.d.ts +1 -1
- package/dist/device/DeviceCommands.d.ts +5 -4
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +19 -12
- package/dist/index.js +171 -141
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +3 -4
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/api/solSignOffchainMessage.d.ts +1 -0
- package/dist/types/api/solSignOffchainMessage.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +9 -4
- package/src/api/UploadPortfolio.ts +5 -4
- package/src/api/solana/SolSignOffchainMessage.ts +44 -4
- package/src/core/RequestQueue.ts +16 -1
- package/src/core/index.ts +43 -20
- package/src/data/messages/messages-protocol-v2.json +28 -33
- package/src/data/messages/messages.json +8 -5
- package/src/device/DeviceCommands.ts +29 -2
- package/src/events/logBlockEvent.ts +6 -75
- package/src/protocols/protocol-v2/walletSession.ts +11 -2
- package/src/types/api/protocolV2.ts +3 -4
- package/src/types/api/solSignOffchainMessage.ts +2 -0
package/src/core/RequestQueue.ts
CHANGED
|
@@ -56,11 +56,20 @@ export default class RequestQueue {
|
|
|
56
56
|
return false;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
private isRequestForConnectId(request: RequestTask, connectId: string) {
|
|
60
|
+
const { method } = request;
|
|
61
|
+
return (
|
|
62
|
+
method.connectId === connectId ||
|
|
63
|
+
method.device?.mainId === connectId ||
|
|
64
|
+
method.device?.getConnectId() === connectId
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
// 取消与指定connectId相关的所有请求
|
|
60
69
|
public abortRequestsByConnectId(connectId: string) {
|
|
61
70
|
let count = 0;
|
|
62
71
|
this.requestQueue.forEach((request, _) => {
|
|
63
|
-
if (request.abortController && request
|
|
72
|
+
if (request.abortController && this.isRequestForConnectId(request, connectId)) {
|
|
64
73
|
request.abortController.abort();
|
|
65
74
|
request.abortController = undefined;
|
|
66
75
|
count++;
|
|
@@ -69,6 +78,12 @@ export default class RequestQueue {
|
|
|
69
78
|
return count;
|
|
70
79
|
}
|
|
71
80
|
|
|
81
|
+
public getRequestTasksIdByConnectId(connectId: string) {
|
|
82
|
+
return Array.from(this.requestQueue.values())
|
|
83
|
+
.filter(request => this.isRequestForConnectId(request, connectId))
|
|
84
|
+
.map(request => request.id);
|
|
85
|
+
}
|
|
86
|
+
|
|
72
87
|
// 取消所有请求
|
|
73
88
|
public abortAllRequests() {
|
|
74
89
|
let count = 0;
|
package/src/core/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
createNeedUpgradeFirmwareHardwareError,
|
|
23
23
|
createNewFirmwareForceUpdateHardwareError,
|
|
24
24
|
createNewFirmwareUnReleaseHardwareError,
|
|
25
|
+
isBleStaleBondHardwareError,
|
|
25
26
|
} from '@onekeyfe/hd-shared';
|
|
26
27
|
|
|
27
28
|
import { LoggerNames, enableLog, getLogger, setLoggerPostMessage, wait } from '../utils';
|
|
@@ -945,7 +946,16 @@ export function isRetryableBleConnectionError(method: BaseMethod, error: unknown
|
|
|
945
946
|
if (method.device?.wasInterruptedByUser()) {
|
|
946
947
|
return false;
|
|
947
948
|
}
|
|
948
|
-
const typedError = error as {
|
|
949
|
+
const typedError = error as {
|
|
950
|
+
errorCode?: unknown;
|
|
951
|
+
params?: { acquireDeadlineExceeded?: unknown };
|
|
952
|
+
};
|
|
953
|
+
if (
|
|
954
|
+
typedError?.errorCode === HardwareErrorCode.BleTimeoutError &&
|
|
955
|
+
typedError.params?.acquireDeadlineExceeded === true
|
|
956
|
+
) {
|
|
957
|
+
return false;
|
|
958
|
+
}
|
|
949
959
|
return (
|
|
950
960
|
typedError?.errorCode === HardwareErrorCode.BleTimeoutError ||
|
|
951
961
|
typedError?.errorCode === HardwareErrorCode.BleConnectedError ||
|
|
@@ -964,11 +974,13 @@ export function isMissingDetectedProtocolV2Error(method: BaseMethod, error: unkn
|
|
|
964
974
|
);
|
|
965
975
|
}
|
|
966
976
|
|
|
967
|
-
export function
|
|
977
|
+
export function isTerminalBleStaleBondError(error: unknown) {
|
|
978
|
+
return isBleStaleBondHardwareError(error);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
export function isDeviceIdentityMismatchError(error: unknown) {
|
|
968
982
|
return (
|
|
969
|
-
|
|
970
|
-
(error as { errorCode?: unknown })?.errorCode ===
|
|
971
|
-
HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
983
|
+
(error as { errorCode?: unknown })?.errorCode === HardwareErrorCode.DeviceCheckDeviceIdError
|
|
972
984
|
);
|
|
973
985
|
}
|
|
974
986
|
|
|
@@ -1001,7 +1013,8 @@ function raceBleAcquire<T>(acquirePromise: Promise<T>, abortSignal?: AbortSignal
|
|
|
1001
1013
|
reject(
|
|
1002
1014
|
ERRORS.TypedError(
|
|
1003
1015
|
HardwareErrorCode.BleTimeoutError,
|
|
1004
|
-
`BLE acquire exceeded ${BLE_ACQUIRE_DEADLINE_MS}ms deadline
|
|
1016
|
+
`BLE acquire exceeded ${BLE_ACQUIRE_DEADLINE_MS}ms deadline`,
|
|
1017
|
+
{ acquireDeadlineExceeded: true }
|
|
1005
1018
|
)
|
|
1006
1019
|
)
|
|
1007
1020
|
),
|
|
@@ -1283,7 +1296,8 @@ const ensureConnected = async (
|
|
|
1283
1296
|
HardwareErrorCode.DeviceInterruptedFromUser,
|
|
1284
1297
|
HardwareErrorCode.CallQueueActionCancelled,
|
|
1285
1298
|
].includes(error.errorCode) ||
|
|
1286
|
-
|
|
1299
|
+
isTerminalBleStaleBondError(error) ||
|
|
1300
|
+
isDeviceIdentityMismatchError(error)
|
|
1287
1301
|
) {
|
|
1288
1302
|
reject(error);
|
|
1289
1303
|
return;
|
|
@@ -1333,7 +1347,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1333
1347
|
// cancel callback tasks
|
|
1334
1348
|
requestQueue.cancelCallbackTasks(connectId);
|
|
1335
1349
|
|
|
1336
|
-
const requestIds = requestQueue.
|
|
1350
|
+
const requestIds = requestQueue.getRequestTasksIdByConnectId(connectId);
|
|
1337
1351
|
Log.debug(
|
|
1338
1352
|
`Cancel Api connect requestQueues: length:${requestIds.length} requestIds:${requestIds.join(
|
|
1339
1353
|
','
|
|
@@ -1341,10 +1355,9 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1341
1355
|
);
|
|
1342
1356
|
// Abort before rejecting: rejectRequest releases the task and would make
|
|
1343
1357
|
// its AbortController unreachable to an in-flight method loop.
|
|
1344
|
-
//
|
|
1345
|
-
//
|
|
1346
|
-
|
|
1347
|
-
requestQueue.abortAllRequests();
|
|
1358
|
+
// Match both the requested connectId and a device selected internally by the
|
|
1359
|
+
// method, such as Desktop WebUSB firmwareUpdateV4.
|
|
1360
|
+
requestQueue.abortRequestsByConnectId(connectId);
|
|
1348
1361
|
const canceledDevices: Device[] = [];
|
|
1349
1362
|
const interruptDevice = (device: Device | undefined, deviceConnectId: string) => {
|
|
1350
1363
|
if (!device || canceledDevices.includes(device)) {
|
|
@@ -1360,7 +1373,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1360
1373
|
// During ensureConnected the method has a connectId but device is
|
|
1361
1374
|
// assigned only after the poll succeeds. Interrupt the cached BLE
|
|
1362
1375
|
// Device so an in-flight acquire/initialize cannot finish.
|
|
1363
|
-
interruptDevice(task.method?.device,
|
|
1376
|
+
interruptDevice(task.method?.device, connectId);
|
|
1364
1377
|
interruptDevice(deviceCacheMap.get(connectId), connectId);
|
|
1365
1378
|
requestQueue.rejectRequest(
|
|
1366
1379
|
requestId,
|
|
@@ -1375,10 +1388,11 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1375
1388
|
}
|
|
1376
1389
|
} else {
|
|
1377
1390
|
const env = DataManager.getSettings('env');
|
|
1391
|
+
// Abort every method before rejecting its queue task. Non-BLE methods also
|
|
1392
|
+
// use the signal to stop recovery loops after the public promise is rejected.
|
|
1393
|
+
requestQueue.abortAllRequests();
|
|
1378
1394
|
if (DataManager.isBleConnect(env)) {
|
|
1379
1395
|
Log.debug('Cancel Api all _deviceList: ');
|
|
1380
|
-
// Keep method abort signals observable until every active task is rejected.
|
|
1381
|
-
requestQueue.abortAllRequests();
|
|
1382
1396
|
const canceledDevices: Device[] = [];
|
|
1383
1397
|
const interruptDevice = (device?: Device) => {
|
|
1384
1398
|
if (!device || canceledDevices.includes(device)) {
|
|
@@ -1421,8 +1435,10 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1421
1435
|
}
|
|
1422
1436
|
}
|
|
1423
1437
|
|
|
1424
|
-
cleanup();
|
|
1425
|
-
|
|
1438
|
+
cleanup(connectId);
|
|
1439
|
+
if (!connectId || _uiPromises.length === 0) {
|
|
1440
|
+
closePopup();
|
|
1441
|
+
}
|
|
1426
1442
|
};
|
|
1427
1443
|
|
|
1428
1444
|
const checkPassphraseEnableState = (method: BaseMethod, features?: Features) => {
|
|
@@ -1460,9 +1476,16 @@ const shouldCheckPassphraseState = (method: BaseMethod, device: Device) => {
|
|
|
1460
1476
|
return device.hasUsePassphrase();
|
|
1461
1477
|
};
|
|
1462
1478
|
|
|
1463
|
-
const cleanup = () => {
|
|
1464
|
-
const pendingUiPromises =
|
|
1465
|
-
|
|
1479
|
+
const cleanup = (connectId?: string) => {
|
|
1480
|
+
const pendingUiPromises = connectId
|
|
1481
|
+
? _uiPromises.filter(
|
|
1482
|
+
uiPromise =>
|
|
1483
|
+
uiPromise.data?.mainId === connectId || uiPromise.data?.getConnectId() === connectId
|
|
1484
|
+
)
|
|
1485
|
+
: _uiPromises;
|
|
1486
|
+
_uiPromises = connectId
|
|
1487
|
+
? _uiPromises.filter(uiPromise => !pendingUiPromises.includes(uiPromise))
|
|
1488
|
+
: [];
|
|
1466
1489
|
rejectUiPromises(
|
|
1467
1490
|
pendingUiPromises,
|
|
1468
1491
|
ERRORS.TypedError(HardwareErrorCode.ActionCancelled, 'UI request was cancelled')
|
|
@@ -4463,10 +4463,6 @@
|
|
|
4463
4463
|
"request_id": {
|
|
4464
4464
|
"type": "bytes",
|
|
4465
4465
|
"id": 5
|
|
4466
|
-
},
|
|
4467
|
-
"source_fingerprint": {
|
|
4468
|
-
"type": "uint32",
|
|
4469
|
-
"id": 6
|
|
4470
4466
|
}
|
|
4471
4467
|
}
|
|
4472
4468
|
},
|
|
@@ -4722,10 +4718,6 @@
|
|
|
4722
4718
|
"expected_address": {
|
|
4723
4719
|
"type": "bytes",
|
|
4724
4720
|
"id": 12
|
|
4725
|
-
},
|
|
4726
|
-
"source_fingerprint": {
|
|
4727
|
-
"type": "uint32",
|
|
4728
|
-
"id": 13
|
|
4729
4721
|
}
|
|
4730
4722
|
}
|
|
4731
4723
|
},
|
|
@@ -4810,10 +4802,6 @@
|
|
|
4810
4802
|
"expected_address": {
|
|
4811
4803
|
"type": "bytes",
|
|
4812
4804
|
"id": 12
|
|
4813
|
-
},
|
|
4814
|
-
"source_fingerprint": {
|
|
4815
|
-
"type": "uint32",
|
|
4816
|
-
"id": 13
|
|
4817
4805
|
}
|
|
4818
4806
|
}
|
|
4819
4807
|
},
|
|
@@ -4961,7 +4949,14 @@
|
|
|
4961
4949
|
"type": "EthereumAuthorizationSignature",
|
|
4962
4950
|
"id": 10
|
|
4963
4951
|
}
|
|
4964
|
-
}
|
|
4952
|
+
},
|
|
4953
|
+
"reserved": [
|
|
4954
|
+
[5, 5],
|
|
4955
|
+
[6, 6],
|
|
4956
|
+
[7, 7],
|
|
4957
|
+
[8, 8],
|
|
4958
|
+
[9, 9]
|
|
4959
|
+
]
|
|
4965
4960
|
},
|
|
4966
4961
|
"EthereumTxAckOneKey": {
|
|
4967
4962
|
"fields": {
|
|
@@ -4990,10 +4985,6 @@
|
|
|
4990
4985
|
"chain_id": {
|
|
4991
4986
|
"type": "uint64",
|
|
4992
4987
|
"id": 3
|
|
4993
|
-
},
|
|
4994
|
-
"source_fingerprint": {
|
|
4995
|
-
"type": "uint32",
|
|
4996
|
-
"id": 4
|
|
4997
4988
|
}
|
|
4998
4989
|
}
|
|
4999
4990
|
},
|
|
@@ -8968,7 +8959,8 @@
|
|
|
8968
8959
|
},
|
|
8969
8960
|
"SolanaOffChainMessageVersion": {
|
|
8970
8961
|
"values": {
|
|
8971
|
-
"MESSAGE_VERSION_0": 0
|
|
8962
|
+
"MESSAGE_VERSION_0": 0,
|
|
8963
|
+
"MESSAGE_VERSION_1": 1
|
|
8972
8964
|
}
|
|
8973
8965
|
},
|
|
8974
8966
|
"SolanaOffChainMessageFormat": {
|
|
@@ -9016,10 +9008,6 @@
|
|
|
9016
9008
|
"rule": "required",
|
|
9017
9009
|
"type": "bytes",
|
|
9018
9010
|
"id": 2
|
|
9019
|
-
},
|
|
9020
|
-
"source_fingerprint": {
|
|
9021
|
-
"type": "uint32",
|
|
9022
|
-
"id": 3
|
|
9023
9011
|
}
|
|
9024
9012
|
}
|
|
9025
9013
|
},
|
|
@@ -9056,17 +9044,15 @@
|
|
|
9056
9044
|
},
|
|
9057
9045
|
"message_format": {
|
|
9058
9046
|
"type": "SolanaOffChainMessageFormat",
|
|
9059
|
-
"id": 4
|
|
9060
|
-
"options": {
|
|
9061
|
-
"default": "V0_RESTRICTED_ASCII"
|
|
9062
|
-
}
|
|
9047
|
+
"id": 4
|
|
9063
9048
|
},
|
|
9064
9049
|
"application_domain": {
|
|
9065
9050
|
"type": "bytes",
|
|
9066
9051
|
"id": 5
|
|
9067
9052
|
},
|
|
9068
|
-
"
|
|
9069
|
-
"
|
|
9053
|
+
"required_signers": {
|
|
9054
|
+
"rule": "repeated",
|
|
9055
|
+
"type": "bytes",
|
|
9070
9056
|
"id": 6
|
|
9071
9057
|
}
|
|
9072
9058
|
}
|
|
@@ -9085,10 +9071,6 @@
|
|
|
9085
9071
|
"rule": "required",
|
|
9086
9072
|
"type": "bytes",
|
|
9087
9073
|
"id": 2
|
|
9088
|
-
},
|
|
9089
|
-
"source_fingerprint": {
|
|
9090
|
-
"type": "uint32",
|
|
9091
|
-
"id": 3
|
|
9092
9074
|
}
|
|
9093
9075
|
}
|
|
9094
9076
|
},
|
|
@@ -11885,6 +11867,10 @@
|
|
|
11885
11867
|
"manufacture_time": {
|
|
11886
11868
|
"type": "DeviceFactoryInfoManufactureTime",
|
|
11887
11869
|
"id": 5
|
|
11870
|
+
},
|
|
11871
|
+
"data": {
|
|
11872
|
+
"type": "bytes",
|
|
11873
|
+
"id": 100
|
|
11888
11874
|
}
|
|
11889
11875
|
}
|
|
11890
11876
|
},
|
|
@@ -11894,11 +11880,20 @@
|
|
|
11894
11880
|
"rule": "required",
|
|
11895
11881
|
"type": "DeviceFactoryInfo",
|
|
11896
11882
|
"id": 1
|
|
11883
|
+
},
|
|
11884
|
+
"full_data": {
|
|
11885
|
+
"type": "bool",
|
|
11886
|
+
"id": 2
|
|
11897
11887
|
}
|
|
11898
11888
|
}
|
|
11899
11889
|
},
|
|
11900
11890
|
"DeviceFactoryInfoGet": {
|
|
11901
|
-
"fields": {
|
|
11891
|
+
"fields": {
|
|
11892
|
+
"full_data": {
|
|
11893
|
+
"type": "bool",
|
|
11894
|
+
"id": 1
|
|
11895
|
+
}
|
|
11896
|
+
}
|
|
11902
11897
|
},
|
|
11903
11898
|
"DeviceFactoryPermanentLock": {
|
|
11904
11899
|
"fields": {
|
|
@@ -10243,7 +10243,8 @@
|
|
|
10243
10243
|
},
|
|
10244
10244
|
"SolanaOffChainMessageVersion": {
|
|
10245
10245
|
"values": {
|
|
10246
|
-
"MESSAGE_VERSION_0": 0
|
|
10246
|
+
"MESSAGE_VERSION_0": 0,
|
|
10247
|
+
"MESSAGE_VERSION_1": 1
|
|
10247
10248
|
}
|
|
10248
10249
|
},
|
|
10249
10250
|
"SolanaOffChainMessageFormat": {
|
|
@@ -10276,14 +10277,16 @@
|
|
|
10276
10277
|
},
|
|
10277
10278
|
"message_format": {
|
|
10278
10279
|
"type": "SolanaOffChainMessageFormat",
|
|
10279
|
-
"id": 4
|
|
10280
|
-
"options": {
|
|
10281
|
-
"default": "V0_RESTRICTED_ASCII"
|
|
10282
|
-
}
|
|
10280
|
+
"id": 4
|
|
10283
10281
|
},
|
|
10284
10282
|
"application_domain": {
|
|
10285
10283
|
"type": "bytes",
|
|
10286
10284
|
"id": 5
|
|
10285
|
+
},
|
|
10286
|
+
"required_signers": {
|
|
10287
|
+
"rule": "repeated",
|
|
10288
|
+
"type": "bytes",
|
|
10289
|
+
"id": 7
|
|
10287
10290
|
}
|
|
10288
10291
|
}
|
|
10289
10292
|
},
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from '@onekeyfe/hd-transport';
|
|
12
12
|
|
|
13
13
|
import TransportManager from '../data-manager/TransportManager';
|
|
14
|
-
import { LoggerNames, getLogger, patchFeatures } from '../utils';
|
|
14
|
+
import { LoggerNames, getLogger, patchFeatures, wait } from '../utils';
|
|
15
15
|
import { DEVICE, type PassphraseRequestPayload } from '../events';
|
|
16
16
|
import { DeviceModelToTypes } from '../types';
|
|
17
17
|
import {
|
|
@@ -198,6 +198,8 @@ export class DeviceCommands {
|
|
|
198
198
|
|
|
199
199
|
disposed: boolean;
|
|
200
200
|
|
|
201
|
+
private disposalToken?: object;
|
|
202
|
+
|
|
201
203
|
callPromise?: Promise<DefaultMessageResponse>;
|
|
202
204
|
|
|
203
205
|
constructor(device: Device, mainId: string) {
|
|
@@ -212,6 +214,7 @@ export class DeviceCommands {
|
|
|
212
214
|
|
|
213
215
|
async dispose(_cancelRequest: boolean) {
|
|
214
216
|
this.disposed = true;
|
|
217
|
+
this.disposalToken = {};
|
|
215
218
|
await this.transport.cancel?.();
|
|
216
219
|
}
|
|
217
220
|
|
|
@@ -439,7 +442,31 @@ export class DeviceCommands {
|
|
|
439
442
|
msg?: DefaultMessageResponse['message'],
|
|
440
443
|
options?: TransportCallOptions
|
|
441
444
|
) {
|
|
442
|
-
const
|
|
445
|
+
const { disposalToken } = this;
|
|
446
|
+
let resp = await this.call(type, msg, options);
|
|
447
|
+
// Session busy failures precede wallet derivation or reject an occupied flow.
|
|
448
|
+
// Three retries cover the 240 ms PIN dismissal animation on older V2 firmware.
|
|
449
|
+
for (
|
|
450
|
+
let retry = 0;
|
|
451
|
+
retry < 3 &&
|
|
452
|
+
this.device.isProtocolV2() &&
|
|
453
|
+
DEVICE_SESSION_CALLS.has(type) &&
|
|
454
|
+
resp.type === 'Failure' &&
|
|
455
|
+
(resp.message.code as string | FailureType) === 'Failure_ProcessError' &&
|
|
456
|
+
resp.message.subcode === DeviceSessionErrorCode.DeviceSessionError_Busy;
|
|
457
|
+
retry += 1
|
|
458
|
+
) {
|
|
459
|
+
await wait(100);
|
|
460
|
+
if (this.device.wasInterruptedByUser()) {
|
|
461
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromUser);
|
|
462
|
+
}
|
|
463
|
+
this.checkDisposed();
|
|
464
|
+
if (this.disposalToken !== disposalToken) {
|
|
465
|
+
// React Native can revive this instance. A stale retry must not release the new run.
|
|
466
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'DeviceCommands lifecycle changed');
|
|
467
|
+
}
|
|
468
|
+
resp = await this.call(type, msg, options);
|
|
469
|
+
}
|
|
443
470
|
return this._filterCommonTypes(resp, type, options);
|
|
444
471
|
}
|
|
445
472
|
|
|
@@ -8,8 +8,9 @@ export const LogBlockEvent: Set<string> = new Set([
|
|
|
8
8
|
UI_RESPONSE.RECEIVE_PASSPHRASE,
|
|
9
9
|
]);
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
// These resource/file APIs did not exist in 1.1.32. Skip the payload so
|
|
12
|
+
// the log layer does not copy huge Base64 or binary data.
|
|
13
|
+
const LogPayloadBlockMethod: Set<string> = new Set([
|
|
13
14
|
'deviceUploadNft',
|
|
14
15
|
'deviceUploadWallpaper',
|
|
15
16
|
'uploadPortfolio',
|
|
@@ -17,61 +18,6 @@ const LogLabelMethod: Set<string> = new Set([
|
|
|
17
18
|
'fileRead',
|
|
18
19
|
]);
|
|
19
20
|
|
|
20
|
-
// 资源上传参数可能包含很大的 Base64 字符串。这里按方法整段跳过,避免日志层
|
|
21
|
-
// 递归复制和序列化这些数据;资源 API 与传输内容本身保持不变。
|
|
22
|
-
const LogPayloadBlockMethod: Set<string> = new Set([
|
|
23
|
-
'deviceUploadNft',
|
|
24
|
-
'deviceUploadWallpaper',
|
|
25
|
-
'uploadPortfolio',
|
|
26
|
-
]);
|
|
27
|
-
|
|
28
|
-
const SensitiveLogKeys: Set<string> = new Set([
|
|
29
|
-
'devicestate',
|
|
30
|
-
'entropy',
|
|
31
|
-
'expectedpassphrasestate',
|
|
32
|
-
'mnemonic',
|
|
33
|
-
'passphrase',
|
|
34
|
-
'passphrasestate',
|
|
35
|
-
'password',
|
|
36
|
-
'pin',
|
|
37
|
-
'privatekey',
|
|
38
|
-
'seed',
|
|
39
|
-
'session',
|
|
40
|
-
'sessionid',
|
|
41
|
-
'walletsessionid',
|
|
42
|
-
'xprv',
|
|
43
|
-
]);
|
|
44
|
-
|
|
45
|
-
const normalizeLogKey = (key: string) => key.replace(/[_-]/g, '').toLowerCase();
|
|
46
|
-
|
|
47
|
-
const isSigningMethod = (methodName: string) => /sign/i.test(methodName);
|
|
48
|
-
|
|
49
|
-
const redactLogValue = (value: unknown, seen: WeakSet<object>): unknown => {
|
|
50
|
-
if (ArrayBuffer.isView(value)) {
|
|
51
|
-
return `[BINARY:${value.byteLength}]`;
|
|
52
|
-
}
|
|
53
|
-
if (value instanceof ArrayBuffer) {
|
|
54
|
-
return `[BINARY:${value.byteLength}]`;
|
|
55
|
-
}
|
|
56
|
-
if (Array.isArray(value)) {
|
|
57
|
-
return value.map(item => redactLogValue(item, seen));
|
|
58
|
-
}
|
|
59
|
-
if (!value || typeof value !== 'object') return value;
|
|
60
|
-
if (seen.has(value)) return '[CIRCULAR]';
|
|
61
|
-
|
|
62
|
-
seen.add(value);
|
|
63
|
-
const redacted = Object.fromEntries(
|
|
64
|
-
Object.entries(value as Record<string, unknown>).map(([key, item]) => [
|
|
65
|
-
key,
|
|
66
|
-
SensitiveLogKeys.has(normalizeLogKey(key)) && item !== null && item !== undefined
|
|
67
|
-
? '[REDACTED]'
|
|
68
|
-
: redactLogValue(item, seen),
|
|
69
|
-
])
|
|
70
|
-
);
|
|
71
|
-
seen.delete(value);
|
|
72
|
-
return redacted;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
21
|
export function getLogBlockLabel(message: unknown): string | undefined {
|
|
76
22
|
if (!message || typeof message !== 'object') return undefined;
|
|
77
23
|
|
|
@@ -86,7 +32,7 @@ export function getLogBlockLabel(message: unknown): string | undefined {
|
|
|
86
32
|
}
|
|
87
33
|
|
|
88
34
|
const methodName = method ?? payload?.method;
|
|
89
|
-
if (methodName &&
|
|
35
|
+
if (methodName && LogPayloadBlockMethod.has(methodName)) {
|
|
90
36
|
return methodName;
|
|
91
37
|
}
|
|
92
38
|
|
|
@@ -94,25 +40,10 @@ export function getLogBlockLabel(message: unknown): string | undefined {
|
|
|
94
40
|
}
|
|
95
41
|
|
|
96
42
|
export function getSafeLogPayload(value: unknown, blockLabel?: string): unknown {
|
|
97
|
-
if (
|
|
98
|
-
blockLabel &&
|
|
99
|
-
(LogBlockEvent.has(blockLabel) ||
|
|
100
|
-
LogPayloadBlockMethod.has(blockLabel) ||
|
|
101
|
-
isSigningMethod(blockLabel))
|
|
102
|
-
) {
|
|
43
|
+
if (blockLabel) {
|
|
103
44
|
return { method: blockLabel, payload: '[REDACTED]' };
|
|
104
45
|
}
|
|
105
|
-
|
|
106
|
-
const redactedValue = redactLogValue(value, new WeakSet());
|
|
107
|
-
if (
|
|
108
|
-
blockLabel &&
|
|
109
|
-
redactedValue &&
|
|
110
|
-
typeof redactedValue === 'object' &&
|
|
111
|
-
!Array.isArray(redactedValue)
|
|
112
|
-
) {
|
|
113
|
-
return { ...redactedValue, method: blockLabel };
|
|
114
|
-
}
|
|
115
|
-
return redactedValue;
|
|
46
|
+
return value;
|
|
116
47
|
}
|
|
117
48
|
|
|
118
49
|
export function formatLogMethodLabel(label: string, methodName?: string): string {
|
|
@@ -234,9 +234,9 @@ export async function getProtocolV2WalletSession(
|
|
|
234
234
|
const forceWalletSelection =
|
|
235
235
|
options?.forceWalletSelection === true || options?.initSession === true;
|
|
236
236
|
const readCurrentAttachPinSession = options?.readCurrentAttachPinSession === true;
|
|
237
|
-
const sessionIsAttachPinWallet = (session?:
|
|
237
|
+
const sessionIsAttachPinWallet = (session?: unknown) =>
|
|
238
238
|
readCurrentAttachPinSession ||
|
|
239
|
-
session?.viaAttachPin === true ||
|
|
239
|
+
(session as { viaAttachPin?: boolean } | undefined)?.viaAttachPin === true ||
|
|
240
240
|
device.features?.unlockedAttachPin === true;
|
|
241
241
|
|
|
242
242
|
if (forceWalletSelection) {
|
|
@@ -273,6 +273,10 @@ export async function getProtocolV2WalletSession(
|
|
|
273
273
|
const markWalletStatusRefreshed = () => {
|
|
274
274
|
walletStatusRefreshed = true;
|
|
275
275
|
};
|
|
276
|
+
if (options?.onlyMainPin && options.mainPinSelected !== true) {
|
|
277
|
+
await refreshProtocolV2DeviceStatus(device);
|
|
278
|
+
markWalletStatusRefreshed();
|
|
279
|
+
}
|
|
276
280
|
let mainPinAuthenticated =
|
|
277
281
|
options?.mainPinSelected === true ||
|
|
278
282
|
(options?.onlyMainPin === true &&
|
|
@@ -451,6 +455,11 @@ export async function getProtocolV2WalletSession(
|
|
|
451
455
|
throw error;
|
|
452
456
|
}
|
|
453
457
|
resumed = false;
|
|
458
|
+
if (device.features?.unlockedAttachPin === true) {
|
|
459
|
+
// Locking invalidates the old handle, but Attach PIN already selected the wallet.
|
|
460
|
+
// Read the current session and validate its passphrase state below.
|
|
461
|
+
response = await getDeviceSession(device, sessionGetRequest());
|
|
462
|
+
}
|
|
454
463
|
}
|
|
455
464
|
} else if (expectedPassphraseState) {
|
|
456
465
|
try {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CommonParams, Response } from '../params';
|
|
2
2
|
import type { OnboardingStatus, Success } from '@onekeyfe/hd-transport';
|
|
3
3
|
import type { DeviceRebootParams } from '../../api/protocol-v2/helpers';
|
|
4
|
+
import type { UploadPortfolioParams } from '../../api/UploadPortfolio';
|
|
4
5
|
import type {
|
|
5
6
|
DeviceUploadWallpaperParams,
|
|
6
7
|
DeviceUploadWallpaperResponse,
|
|
@@ -12,6 +13,7 @@ import type {
|
|
|
12
13
|
|
|
13
14
|
// Re-export implementation parameter types as the single source of truth.
|
|
14
15
|
export type { DeviceRebootParams, RebootTypeInput } from '../../api/protocol-v2/helpers';
|
|
16
|
+
export type { UploadPortfolioParams } from '../../api/UploadPortfolio';
|
|
15
17
|
export type {
|
|
16
18
|
DeviceUploadWallpaperParams,
|
|
17
19
|
DeviceUploadWallpaperResponse,
|
|
@@ -67,8 +69,5 @@ export declare function deviceUploadNft(
|
|
|
67
69
|
|
|
68
70
|
export declare function uploadPortfolio(
|
|
69
71
|
connectId: string,
|
|
70
|
-
params:
|
|
71
|
-
packageBase64: string;
|
|
72
|
-
timeoutMs?: number | string;
|
|
73
|
-
}
|
|
72
|
+
params: UploadPortfolioParams
|
|
74
73
|
): Response<FileInfo & { portfolioUpdated: true }>;
|
|
@@ -15,6 +15,8 @@ export type SolSignOffchainMessageParams = {
|
|
|
15
15
|
messageVersion?: SolanaOffChainMessageVersion;
|
|
16
16
|
messageFormat?: SolanaOffChainMessageFormat;
|
|
17
17
|
applicationDomainHex?: string;
|
|
18
|
+
/** 32-byte public keys encoded as hex, strictly sorted and unique. */
|
|
19
|
+
requiredSigners?: string[];
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
export declare function solSignOffchainMessage(
|