@onekeyfe/hwk-keystone-adapter 1.2.3-alpha.3 → 1.2.3-alpha.8
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/dist/index.d.mts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +406 -277
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +207 -76
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -27,6 +27,7 @@ var TronSignRequest = class _TronSignRequest extends RegistryItem {
|
|
|
27
27
|
this.getSignData = () => this.signData;
|
|
28
28
|
this.getSignType = () => this.signType;
|
|
29
29
|
this.getDerivationPath = () => this.derivationPath.getPath();
|
|
30
|
+
this.getOrigin = () => this.origin;
|
|
30
31
|
this.toDataItem = () => {
|
|
31
32
|
const map = {};
|
|
32
33
|
if (this.requestId) {
|
|
@@ -461,8 +462,10 @@ import {
|
|
|
461
462
|
getAllNetworkMethodChain,
|
|
462
463
|
hasHardwareRuntimeIdPrefix,
|
|
463
464
|
isAllNetworkMethodName,
|
|
464
|
-
|
|
465
|
+
isConnectionLost,
|
|
466
|
+
isHardwareOperationId as isHardwareOperationId2,
|
|
465
467
|
isHwkRecoveryHint,
|
|
468
|
+
isUserRefusal,
|
|
466
469
|
operationMayHaveCompletedParams,
|
|
467
470
|
parseBip32MasterFingerprint as parseBip32MasterFingerprint2,
|
|
468
471
|
rehydrateConnectorError,
|
|
@@ -581,8 +584,29 @@ function placeholderDeviceInfo() {
|
|
|
581
584
|
};
|
|
582
585
|
}
|
|
583
586
|
|
|
587
|
+
// src/utils/queueKey.ts
|
|
588
|
+
import { isHardwareOperationId } from "@onekeyfe/hwk-adapter-core";
|
|
589
|
+
var KEYSTONE_COLD_START_JOB_LABEL = "keystone-cold-start";
|
|
590
|
+
function keystoneQueueKey(connectId, deviceId) {
|
|
591
|
+
return isHardwareOperationId(connectId) ? connectId : deviceId || connectId || KEYSTONE_COLD_START_JOB_LABEL;
|
|
592
|
+
}
|
|
593
|
+
function keystoneWalletIdFromIdentifier(identifier) {
|
|
594
|
+
const value = identifier.startsWith(KEYSTONE_WALLET_CONNECT_ID_PREFIX) ? identifier.slice(KEYSTONE_WALLET_CONNECT_ID_PREFIX.length) : identifier;
|
|
595
|
+
return /^[0-9a-f]{64}$/i.test(value) ? value.toLowerCase() : void 0;
|
|
596
|
+
}
|
|
597
|
+
function keystoneCancelQueueKeys(identifiers) {
|
|
598
|
+
const keys = /* @__PURE__ */ new Set();
|
|
599
|
+
for (const identifier of identifiers) {
|
|
600
|
+
if (identifier) {
|
|
601
|
+
keys.add(identifier);
|
|
602
|
+
const walletId = keystoneWalletIdFromIdentifier(identifier);
|
|
603
|
+
if (walletId) keys.add(walletId);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return keys;
|
|
607
|
+
}
|
|
608
|
+
|
|
584
609
|
// src/adapter/KeystoneAdapter.ts
|
|
585
|
-
var COLD_START_JOB_LABEL = "keystone-cold-start";
|
|
586
610
|
var KEYSTONE_USB_REATTACH_PROBE_ATTEMPTS = 4;
|
|
587
611
|
var KEYSTONE_USB_REATTACH_PROBE_INTERVAL_MS = 500;
|
|
588
612
|
function resolveHardwareOperationTarget(positionalTargetId, operationId) {
|
|
@@ -594,8 +618,11 @@ function waitForKeystoneUsbReattachProbe() {
|
|
|
594
618
|
setTimeout(resolve, KEYSTONE_USB_REATTACH_PROBE_INTERVAL_MS);
|
|
595
619
|
});
|
|
596
620
|
}
|
|
597
|
-
function
|
|
598
|
-
|
|
621
|
+
function buildCancelledFailure(signal) {
|
|
622
|
+
const reason = signal.reason;
|
|
623
|
+
const code = typeof reason?.code === "number" ? reason.code : HardwareErrorCode.UserAborted;
|
|
624
|
+
const message = typeof reason?.message === "string" ? reason.message : "";
|
|
625
|
+
return failure(code, message || "All-network get-address cancelled");
|
|
599
626
|
}
|
|
600
627
|
var BIP44_COIN_TYPE_TO_CHAIN = {
|
|
601
628
|
60: "evm",
|
|
@@ -610,6 +637,9 @@ function inferHwkChainFromPath(path) {
|
|
|
610
637
|
var KeystoneAdapter = class _KeystoneAdapter {
|
|
611
638
|
constructor(options) {
|
|
612
639
|
this.vendor = "keystone";
|
|
640
|
+
// No protocol-level cancel exists over Keystone USB, and QR has no transport
|
|
641
|
+
// to interrupt at all. Cancelling ends the wait, not the device's prompt.
|
|
642
|
+
this.cancelCapability = "stops-waiting";
|
|
613
643
|
this.emitter = new TypedEventEmitter();
|
|
614
644
|
this._operationRoutes = /* @__PURE__ */ new Map();
|
|
615
645
|
this._operations = new OperationRegistry({
|
|
@@ -663,6 +693,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
663
693
|
if (!operationTarget.success) return operationTarget;
|
|
664
694
|
const effectiveConnectId = operationTarget.payload.targetId ?? "";
|
|
665
695
|
const { operationId } = operationTarget.payload;
|
|
696
|
+
let cancelScope;
|
|
666
697
|
try {
|
|
667
698
|
const prefetched = await this._prefetchAllNetworkAccounts(
|
|
668
699
|
effectiveConnectId,
|
|
@@ -671,11 +702,20 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
671
702
|
);
|
|
672
703
|
const { book } = prefetched;
|
|
673
704
|
const itemDeviceId = deviceId || prefetched.walletId || "";
|
|
705
|
+
const scope = this._jobQueue.createCancelScope(
|
|
706
|
+
keystoneQueueKey(effectiveConnectId, itemDeviceId)
|
|
707
|
+
);
|
|
708
|
+
cancelScope = scope;
|
|
709
|
+
const cancelledIndexes = /* @__PURE__ */ new Set();
|
|
674
710
|
return await runAllNetworkGetAddress({
|
|
675
711
|
connectId: effectiveConnectId,
|
|
676
712
|
deviceId: itemDeviceId,
|
|
677
713
|
params,
|
|
678
|
-
callItem: async ({ method, item }) => {
|
|
714
|
+
callItem: async ({ method, item, index }) => {
|
|
715
|
+
if (scope.signal.aborted) {
|
|
716
|
+
cancelledIndexes.add(index);
|
|
717
|
+
return buildCancelledFailure(scope.signal);
|
|
718
|
+
}
|
|
679
719
|
const commonArgs = {
|
|
680
720
|
path: item.path,
|
|
681
721
|
showOnDevice: item.showOnDevice,
|
|
@@ -714,10 +754,19 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
714
754
|
} : void 0
|
|
715
755
|
}
|
|
716
756
|
});
|
|
757
|
+
},
|
|
758
|
+
// The gap check plus the shared top-level table: a refusal or a lost
|
|
759
|
+
// session ends the batch, every other per-item failure keeps its
|
|
760
|
+
// existing "carry on with the next chain" behaviour.
|
|
761
|
+
shouldAbortBundle: (response, { index }) => {
|
|
762
|
+
const { code } = response.payload ?? {};
|
|
763
|
+
return cancelledIndexes.has(index) || isUserRefusal(code) || isConnectionLost(code);
|
|
717
764
|
}
|
|
718
765
|
});
|
|
719
766
|
} catch (err) {
|
|
720
767
|
return this._errorToFailure(err);
|
|
768
|
+
} finally {
|
|
769
|
+
cancelScope?.release();
|
|
721
770
|
}
|
|
722
771
|
};
|
|
723
772
|
this._origin = options?.origin ?? "OneKey";
|
|
@@ -915,6 +964,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
915
964
|
searchTargetId,
|
|
916
965
|
connectId: connected.payload.connectId,
|
|
917
966
|
device: connected.payload,
|
|
967
|
+
connectionType: selectedConnectionType,
|
|
918
968
|
connectionKeys: selectedConnectionType === "usb" && record?.usbSessionId ? [record.usbSessionId] : []
|
|
919
969
|
});
|
|
920
970
|
this._operationRoutes.set(connected.payload.connectId, {
|
|
@@ -967,7 +1017,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
967
1017
|
}
|
|
968
1018
|
getDeviceInfo(connectIdOrOperationId, deviceId) {
|
|
969
1019
|
try {
|
|
970
|
-
const connectId =
|
|
1020
|
+
const connectId = isHardwareOperationId2(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
|
|
971
1021
|
const { record } = this._resolveTarget(connectId, deviceId);
|
|
972
1022
|
if (!record) {
|
|
973
1023
|
return Promise.resolve(
|
|
@@ -983,25 +1033,35 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
983
1033
|
}
|
|
984
1034
|
}
|
|
985
1035
|
cancel(connectId) {
|
|
1036
|
+
let namedOperationConnectId;
|
|
1037
|
+
if (isHardwareOperationId2(connectId)) {
|
|
1038
|
+
try {
|
|
1039
|
+
namedOperationConnectId = this._operations.resolve(connectId).connectId;
|
|
1040
|
+
} catch {
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
986
1044
|
const reason = createHwkError({
|
|
987
1045
|
code: HardwareErrorCode.UserAborted,
|
|
988
1046
|
message: "User aborted operation"
|
|
989
1047
|
});
|
|
990
|
-
const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
|
|
991
1048
|
if (connectId) {
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1049
|
+
const queueKeys = keystoneCancelQueueKeys([
|
|
1050
|
+
connectId,
|
|
1051
|
+
this._operationRoutes.get(connectId)?.operationId,
|
|
1052
|
+
namedOperationConnectId
|
|
1053
|
+
]);
|
|
1054
|
+
for (const key of queueKeys) {
|
|
1055
|
+
this._jobQueue.cancelActiveAndPending(key, reason);
|
|
1056
|
+
}
|
|
1057
|
+
const scopedOperationId = isHardwareOperationId2(connectId) ? connectId : this._operationRouteForIdentifier(connectId)?.operationId;
|
|
1058
|
+
if (scopedOperationId) {
|
|
1059
|
+
this._uiRegistry.cancel(void 0, void 0, scopedOperationId);
|
|
999
1060
|
}
|
|
1000
|
-
this._jobQueue.cancelActiveAndPending(activeJobId, reason);
|
|
1001
1061
|
} else {
|
|
1002
1062
|
this._jobQueue.cancelActiveAndPending(void 0, reason);
|
|
1063
|
+
this._uiRegistry.cancel();
|
|
1003
1064
|
}
|
|
1004
|
-
this._uiRegistry.cancel();
|
|
1005
1065
|
}
|
|
1006
1066
|
getChainFingerprint(connectId, deviceId, chain) {
|
|
1007
1067
|
try {
|
|
@@ -1036,20 +1096,21 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1036
1096
|
// ---------------------------------------------------------------------------
|
|
1037
1097
|
async importFromQr(options = {}) {
|
|
1038
1098
|
try {
|
|
1039
|
-
return await this._jobQueue.enqueue(
|
|
1099
|
+
return await this._jobQueue.enqueue(KEYSTONE_COLD_START_JOB_LABEL, async (signal) => {
|
|
1040
1100
|
const displayDevice = placeholderDeviceInfo();
|
|
1041
1101
|
let responseUr;
|
|
1042
1102
|
if (options.mode === "scan") {
|
|
1043
|
-
responseUr = await this._requestQrScanAndAwaitResponse(displayDevice);
|
|
1103
|
+
responseUr = await this._requestQrScanAndAwaitResponse(displayDevice, signal);
|
|
1044
1104
|
} else {
|
|
1045
1105
|
const requestUr = this.urEngine.buildKeyDerivationRequest({
|
|
1046
1106
|
schemas: [{ path: KEYSTONE_WALLET_ID_PATH, curve: "secp256k1" }],
|
|
1047
1107
|
origin: this._origin
|
|
1048
1108
|
});
|
|
1049
|
-
responseUr = await this._requestQrDisplayAndAwaitResponse(
|
|
1050
|
-
|
|
1051
|
-
animated: false
|
|
1052
|
-
|
|
1109
|
+
responseUr = await this._requestQrDisplayAndAwaitResponse(
|
|
1110
|
+
displayDevice,
|
|
1111
|
+
{ ...requestUr, animated: false },
|
|
1112
|
+
signal
|
|
1113
|
+
);
|
|
1053
1114
|
}
|
|
1054
1115
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1055
1116
|
const parsed = this.urEngine.parseAccountResponse(responseUr);
|
|
@@ -1080,7 +1141,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1080
1141
|
);
|
|
1081
1142
|
}
|
|
1082
1143
|
try {
|
|
1083
|
-
return await this._jobQueue.enqueue(
|
|
1144
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1084
1145
|
const { account } = await this._fetchAccount(
|
|
1085
1146
|
connectId,
|
|
1086
1147
|
deviceId,
|
|
@@ -1126,7 +1187,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1126
1187
|
const rawTxHex = stripHex(params.serializedTx);
|
|
1127
1188
|
const path = normalizePath(params.path);
|
|
1128
1189
|
try {
|
|
1129
|
-
return await this._jobQueue.enqueue(
|
|
1190
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1130
1191
|
const { record } = await this._ensureWalletKnown(connectId, deviceId, "evm", signal);
|
|
1131
1192
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1132
1193
|
const requestId = uuidv4();
|
|
@@ -1137,7 +1198,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1137
1198
|
dataType,
|
|
1138
1199
|
path,
|
|
1139
1200
|
xfp: record.masterFingerprint,
|
|
1140
|
-
chainId: params.chainId
|
|
1201
|
+
chainId: params.chainId,
|
|
1202
|
+
origin: this._origin
|
|
1141
1203
|
});
|
|
1142
1204
|
const responseUr = await this._resolveUr(
|
|
1143
1205
|
record,
|
|
@@ -1176,7 +1238,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1176
1238
|
const messageHex = params.hex ? stripHex(params.message) : Buffer.from(params.message, "utf8").toString("hex");
|
|
1177
1239
|
const path = normalizePath(params.path);
|
|
1178
1240
|
try {
|
|
1179
|
-
return await this._jobQueue.enqueue(
|
|
1241
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1180
1242
|
const { record } = await this._ensureWalletKnown(connectId, deviceId, "evm", signal);
|
|
1181
1243
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1182
1244
|
const requestId = uuidv4();
|
|
@@ -1186,7 +1248,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1186
1248
|
dataType: "personalMessage",
|
|
1187
1249
|
path,
|
|
1188
1250
|
xfp: record.masterFingerprint,
|
|
1189
|
-
chainId: params.chainId
|
|
1251
|
+
chainId: params.chainId,
|
|
1252
|
+
origin: this._origin
|
|
1190
1253
|
});
|
|
1191
1254
|
const responseUr = await this._resolveUr(
|
|
1192
1255
|
record,
|
|
@@ -1228,7 +1291,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1228
1291
|
).toString("hex");
|
|
1229
1292
|
const path = normalizePath(params.path);
|
|
1230
1293
|
try {
|
|
1231
|
-
return await this._jobQueue.enqueue(
|
|
1294
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1232
1295
|
const { record } = await this._ensureWalletKnown(connectId, deviceId, "evm", signal);
|
|
1233
1296
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1234
1297
|
const requestId = uuidv4();
|
|
@@ -1238,7 +1301,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1238
1301
|
dataType: "typedData",
|
|
1239
1302
|
path,
|
|
1240
1303
|
xfp: record.masterFingerprint,
|
|
1241
|
-
chainId: params.chainId
|
|
1304
|
+
chainId: params.chainId,
|
|
1305
|
+
origin: this._origin
|
|
1242
1306
|
});
|
|
1243
1307
|
const responseUr = await this._resolveUr(
|
|
1244
1308
|
record,
|
|
@@ -1297,7 +1361,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1297
1361
|
return failure(HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
|
|
1298
1362
|
}
|
|
1299
1363
|
try {
|
|
1300
|
-
return await this._jobQueue.enqueue(
|
|
1364
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1301
1365
|
const { account } = await this._fetchAccount(
|
|
1302
1366
|
connectId,
|
|
1303
1367
|
deviceId,
|
|
@@ -1353,7 +1417,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1353
1417
|
return failure(HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
|
|
1354
1418
|
}
|
|
1355
1419
|
try {
|
|
1356
|
-
return await this._jobQueue.enqueue(
|
|
1420
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1357
1421
|
const { account } = await this._fetchAccount(
|
|
1358
1422
|
connectId,
|
|
1359
1423
|
deviceId,
|
|
@@ -1398,11 +1462,13 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1398
1462
|
if (!params) return failure(HardwareErrorCode.InvalidParams, "btcSignPsbt requires params");
|
|
1399
1463
|
if (!params.psbt)
|
|
1400
1464
|
return failure(HardwareErrorCode.InvalidParams, "btcSignPsbt requires params.psbt");
|
|
1401
|
-
if (
|
|
1465
|
+
if (!params.path)
|
|
1466
|
+
return failure(HardwareErrorCode.InvalidParams, "btcSignPsbt requires params.path");
|
|
1467
|
+
if (!isKeystoneSignableBtcAccountPath(splitAccountPath(params.path).accountPath)) {
|
|
1402
1468
|
return failure(HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
|
|
1403
1469
|
}
|
|
1404
1470
|
try {
|
|
1405
|
-
return await this._jobQueue.enqueue(
|
|
1471
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1406
1472
|
const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
|
|
1407
1473
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1408
1474
|
const requestUr = this.urEngine.buildBtcPsbtRequest(stripHex(params.psbt));
|
|
@@ -1441,14 +1507,15 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1441
1507
|
return failure(HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
|
|
1442
1508
|
}
|
|
1443
1509
|
try {
|
|
1444
|
-
return await this._jobQueue.enqueue(
|
|
1510
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1445
1511
|
const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
|
|
1446
1512
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1447
1513
|
const requestId = uuidv4();
|
|
1448
1514
|
const requestUr = this.urEngine.buildBtcMessageSignRequest({
|
|
1449
1515
|
requestId,
|
|
1450
1516
|
messageHex,
|
|
1451
|
-
accounts: [{ path, xfp: record.masterFingerprint }]
|
|
1517
|
+
accounts: [{ path, xfp: record.masterFingerprint }],
|
|
1518
|
+
origin: this._origin
|
|
1452
1519
|
});
|
|
1453
1520
|
const responseUr = await this._resolveUr(
|
|
1454
1521
|
record,
|
|
@@ -1473,7 +1540,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1473
1540
|
const connectId = operationTarget.payload.targetId;
|
|
1474
1541
|
const deviceId = deviceIdArg ?? void 0;
|
|
1475
1542
|
try {
|
|
1476
|
-
return await this._jobQueue.enqueue(
|
|
1543
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1477
1544
|
const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
|
|
1478
1545
|
return success({ masterFingerprint: record.masterFingerprint });
|
|
1479
1546
|
});
|
|
@@ -1495,7 +1562,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1495
1562
|
return failure(HardwareErrorCode.InvalidParams, "solGetAddress requires params.path");
|
|
1496
1563
|
const path = normalizePath(params.path);
|
|
1497
1564
|
try {
|
|
1498
|
-
return await this._jobQueue.enqueue(
|
|
1565
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1499
1566
|
const { account } = await this._fetchAccount(connectId, deviceId, "sol", path, signal, {
|
|
1500
1567
|
book
|
|
1501
1568
|
});
|
|
@@ -1523,7 +1590,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1523
1590
|
}
|
|
1524
1591
|
const path = normalizePath(params.path);
|
|
1525
1592
|
try {
|
|
1526
|
-
return await this._jobQueue.enqueue(
|
|
1593
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1527
1594
|
const { record } = await this._ensureWalletKnown(connectId, deviceId, "sol", signal);
|
|
1528
1595
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1529
1596
|
const requestId = uuidv4();
|
|
@@ -1532,7 +1599,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1532
1599
|
unsignedPayloadHex: stripHex(params.serializedTx),
|
|
1533
1600
|
dataType: "transaction",
|
|
1534
1601
|
path,
|
|
1535
|
-
xfp: record.masterFingerprint
|
|
1602
|
+
xfp: record.masterFingerprint,
|
|
1603
|
+
origin: this._origin
|
|
1536
1604
|
});
|
|
1537
1605
|
const responseUr = await this._resolveUr(
|
|
1538
1606
|
record,
|
|
@@ -1566,7 +1634,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1566
1634
|
}
|
|
1567
1635
|
const path = normalizePath(params.path);
|
|
1568
1636
|
try {
|
|
1569
|
-
return await this._jobQueue.enqueue(
|
|
1637
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1570
1638
|
const { record } = await this._ensureWalletKnown(connectId, deviceId, "sol", signal);
|
|
1571
1639
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1572
1640
|
const requestId = uuidv4();
|
|
@@ -1575,7 +1643,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1575
1643
|
unsignedPayloadHex: stripHex(params.message),
|
|
1576
1644
|
dataType: "message",
|
|
1577
1645
|
path,
|
|
1578
|
-
xfp: record.masterFingerprint
|
|
1646
|
+
xfp: record.masterFingerprint,
|
|
1647
|
+
origin: this._origin
|
|
1579
1648
|
});
|
|
1580
1649
|
const responseUr = await this._resolveUr(
|
|
1581
1650
|
record,
|
|
@@ -1596,10 +1665,11 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1596
1665
|
}
|
|
1597
1666
|
// ---------------------------------------------------------------------------
|
|
1598
1667
|
// TRON — routed through `TronSignRequest`/`TronSignature` (see
|
|
1599
|
-
// urEngine/TronSignRequest.ts),
|
|
1600
|
-
//
|
|
1601
|
-
//
|
|
1602
|
-
// unverified response
|
|
1668
|
+
// urEngine/TronSignRequest.ts), whose key layout follows keystone-sdk-rust
|
|
1669
|
+
// (tags 5201/5202, firmware >= 2.5.0). The OneKey Pro air-gap encoding under
|
|
1670
|
+
// the same tag is a different layout the firmware rejects. Not keystone-sdk's
|
|
1671
|
+
// bundled `sdk.tron` module either (protobuf-based, unverified response
|
|
1672
|
+
// semantics).
|
|
1603
1673
|
// ---------------------------------------------------------------------------
|
|
1604
1674
|
async tronGetAddress(connectIdArg, deviceIdArg, paramsArg, book) {
|
|
1605
1675
|
const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
|
|
@@ -1618,7 +1688,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1618
1688
|
);
|
|
1619
1689
|
}
|
|
1620
1690
|
try {
|
|
1621
|
-
return await this._jobQueue.enqueue(
|
|
1691
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1622
1692
|
const { account } = await this._fetchAccount(
|
|
1623
1693
|
connectId,
|
|
1624
1694
|
deviceId,
|
|
@@ -1664,7 +1734,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1664
1734
|
const rawTxHex = stripHex(params.rawTxHex);
|
|
1665
1735
|
const path = normalizePath(params.path);
|
|
1666
1736
|
try {
|
|
1667
|
-
return await this._jobQueue.enqueue(
|
|
1737
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1668
1738
|
const { record } = await this._ensureWalletKnown(connectId, deviceId, "tron", signal);
|
|
1669
1739
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1670
1740
|
const requestId = uuidv4();
|
|
@@ -1672,7 +1742,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1672
1742
|
requestId,
|
|
1673
1743
|
rawTxHex,
|
|
1674
1744
|
path,
|
|
1675
|
-
xfp: record.masterFingerprint
|
|
1745
|
+
xfp: record.masterFingerprint,
|
|
1746
|
+
origin: this._origin
|
|
1676
1747
|
});
|
|
1677
1748
|
const responseUr = await this._resolveUr(
|
|
1678
1749
|
record,
|
|
@@ -1713,7 +1784,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1713
1784
|
const messageHex = stripHex(params.messageHex);
|
|
1714
1785
|
const path = normalizePath(params.path);
|
|
1715
1786
|
try {
|
|
1716
|
-
return await this._jobQueue.enqueue(
|
|
1787
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1717
1788
|
const { record } = await this._ensureWalletKnown(connectId, deviceId, "tron", signal);
|
|
1718
1789
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1719
1790
|
const requestId = uuidv4();
|
|
@@ -1722,7 +1793,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1722
1793
|
rawTxHex: messageHex,
|
|
1723
1794
|
path,
|
|
1724
1795
|
xfp: record.masterFingerprint,
|
|
1725
|
-
signType: 2 /* PersonalMessage
|
|
1796
|
+
signType: 2 /* PersonalMessage */,
|
|
1797
|
+
origin: this._origin
|
|
1726
1798
|
});
|
|
1727
1799
|
const responseUr = await this._resolveUr(
|
|
1728
1800
|
record,
|
|
@@ -1745,8 +1817,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1745
1817
|
// Internals
|
|
1746
1818
|
// ---------------------------------------------------------------------------
|
|
1747
1819
|
_walletIdFromIdentifier(identifier) {
|
|
1748
|
-
|
|
1749
|
-
return /^[0-9a-f]{64}$/i.test(value) ? value.toLowerCase() : void 0;
|
|
1820
|
+
return keystoneWalletIdFromIdentifier(identifier);
|
|
1750
1821
|
}
|
|
1751
1822
|
_allNetworkSyncSchema(item) {
|
|
1752
1823
|
if (!isAllNetworkMethodName(String(item.methodName)) || !item.path) return void 0;
|
|
@@ -1775,7 +1846,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1775
1846
|
* one-path-at-a-time export flow because the device limits that channel.
|
|
1776
1847
|
*/
|
|
1777
1848
|
async _prefetchAllNetworkAccounts(connectId, deviceId, params) {
|
|
1778
|
-
return this._jobQueue.enqueue(
|
|
1849
|
+
return this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1779
1850
|
const target = this._resolveTarget(connectId, deviceId);
|
|
1780
1851
|
const { record: targetRecord } = target;
|
|
1781
1852
|
let record = targetRecord;
|
|
@@ -1788,14 +1859,17 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1788
1859
|
if (!requestedByKey.size) {
|
|
1789
1860
|
return { book };
|
|
1790
1861
|
}
|
|
1791
|
-
|
|
1862
|
+
const operation = isHardwareOperationId2(params.operationId) ? this._operations.find(params.operationId) : void 0;
|
|
1863
|
+
const operationRoute = operation ? this._operationRoutes.get(operation.connectId) : void 0;
|
|
1864
|
+
const routedToQr = operationRoute?.operationId === operation?.operationId && operationRoute?.connectionType === "qr";
|
|
1865
|
+
if (this._forcedTransport !== "qr" && !routedToQr && !record?.usbSessionId && this._usbConnector && target.expectedWalletId) {
|
|
1792
1866
|
record = await this._tryUsbAttach(
|
|
1793
1867
|
target.expectedWalletId,
|
|
1794
1868
|
record?.masterFingerprint ?? target.expectedMasterFingerprint
|
|
1795
1869
|
) ?? record;
|
|
1796
1870
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1797
1871
|
}
|
|
1798
|
-
if (this._forcedTransport !== "qr" && record?.usbSessionId) {
|
|
1872
|
+
if (this._forcedTransport !== "qr" && !routedToQr && record?.usbSessionId) {
|
|
1799
1873
|
const bundleSessionId = record.usbSessionId;
|
|
1800
1874
|
const assertBundleSession = () => {
|
|
1801
1875
|
if (record?.usbSessionId !== bundleSessionId) {
|
|
@@ -1851,7 +1925,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1851
1925
|
});
|
|
1852
1926
|
const responseUr = await this._requestQrDisplayAndAwaitResponse(
|
|
1853
1927
|
record ? toDeviceInfo(record) : placeholderDeviceInfo(),
|
|
1854
|
-
{ ...requestUr, animated: false }
|
|
1928
|
+
{ ...requestUr, animated: false },
|
|
1929
|
+
signal
|
|
1855
1930
|
);
|
|
1856
1931
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1857
1932
|
const parsed = this.urEngine.parseAccountResponse(responseUr);
|
|
@@ -1887,7 +1962,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1887
1962
|
*/
|
|
1888
1963
|
_resolveTarget(connectId, deviceId) {
|
|
1889
1964
|
let resolvedConnectId = connectId;
|
|
1890
|
-
if (
|
|
1965
|
+
if (isHardwareOperationId2(connectId)) {
|
|
1891
1966
|
resolvedConnectId = this._operations.resolve(connectId).connectId;
|
|
1892
1967
|
}
|
|
1893
1968
|
const identifiers = [deviceId, resolvedConnectId].filter(
|
|
@@ -2137,7 +2212,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2137
2212
|
* closed rather than silently falling back to QR.
|
|
2138
2213
|
*/
|
|
2139
2214
|
async _resolveUr(record, requestUr, animated, operationId, signal, operationName) {
|
|
2140
|
-
const releaseOperationRetention =
|
|
2215
|
+
const releaseOperationRetention = isHardwareOperationId2(operationId) ? this._operations.retain(operationId) : void 0;
|
|
2141
2216
|
try {
|
|
2142
2217
|
return await this._resolveUrWithRoute(
|
|
2143
2218
|
record,
|
|
@@ -2153,7 +2228,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2153
2228
|
}
|
|
2154
2229
|
async _resolveUrWithRoute(record, requestUr, animated, operationId, signal, operationName) {
|
|
2155
2230
|
let interactionRoute;
|
|
2156
|
-
if (
|
|
2231
|
+
if (isHardwareOperationId2(operationId)) {
|
|
2157
2232
|
const operation = this._operations.resolve(operationId);
|
|
2158
2233
|
const route = this._operationRoutes.get(operation.connectId);
|
|
2159
2234
|
if (!route || route.operationId !== operationId) {
|
|
@@ -2202,10 +2277,11 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2202
2277
|
const usbErrorCode = usbError.code;
|
|
2203
2278
|
if (!interactionRoute && !this._forcedTransport && usbErrorCode === HardwareErrorCode.PayloadTooLarge && usbErrorOrigin !== "device") {
|
|
2204
2279
|
const displayDevice2 = toDeviceInfo(record);
|
|
2205
|
-
return this._requestQrDisplayAndAwaitResponse(
|
|
2206
|
-
|
|
2207
|
-
animated
|
|
2208
|
-
|
|
2280
|
+
return this._requestQrDisplayAndAwaitResponse(
|
|
2281
|
+
displayDevice2,
|
|
2282
|
+
{ ...requestUr, animated },
|
|
2283
|
+
signal
|
|
2284
|
+
);
|
|
2209
2285
|
}
|
|
2210
2286
|
const deviceAnswered = usbErrorOrigin !== void 0 ? usbErrorOrigin === "device" : usbErrorCode === HardwareErrorCode.UserRejected || usbErrorCode === HardwareErrorCode.UserAborted || usbErrorCode === HardwareErrorCode.DeviceLocked || usbErrorCode === HardwareErrorCode.DeviceMismatch;
|
|
2211
2287
|
if (!deviceAnswered) {
|
|
@@ -2243,10 +2319,11 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2243
2319
|
}
|
|
2244
2320
|
const displayDevice = record ? toDeviceInfo(record) : placeholderDeviceInfo();
|
|
2245
2321
|
try {
|
|
2246
|
-
return await this._requestQrDisplayAndAwaitResponse(
|
|
2247
|
-
|
|
2248
|
-
animated
|
|
2249
|
-
|
|
2322
|
+
return await this._requestQrDisplayAndAwaitResponse(
|
|
2323
|
+
displayDevice,
|
|
2324
|
+
{ ...requestUr, animated },
|
|
2325
|
+
signal
|
|
2326
|
+
);
|
|
2250
2327
|
} catch (error) {
|
|
2251
2328
|
const { code, _tag: tag } = error;
|
|
2252
2329
|
if (operationName && (code === HardwareErrorCode.OperationTimeout || tag === UI_REQUEST_TIMEOUT_TAG)) {
|
|
@@ -2381,28 +2458,76 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2381
2458
|
const record = this._upsertDeviceRecord(parsed);
|
|
2382
2459
|
return { record };
|
|
2383
2460
|
}
|
|
2384
|
-
|
|
2461
|
+
/**
|
|
2462
|
+
* The operation the running device job belongs to, so a UI request raised
|
|
2463
|
+
* mid-call can name it. A call pinned to an operation queues under its id
|
|
2464
|
+
* (`keystoneQueueKey`); a call without one queues under the wallet id or the
|
|
2465
|
+
* wallet connectId, and the live operation routed to that connection is still
|
|
2466
|
+
* the owner. Only a cold start comes back undefined.
|
|
2467
|
+
*/
|
|
2468
|
+
_activeOperationId() {
|
|
2469
|
+
const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
|
|
2470
|
+
if (!activeJobId) return void 0;
|
|
2471
|
+
if (isHardwareOperationId2(activeJobId)) return activeJobId;
|
|
2472
|
+
return this._operationRouteForIdentifier(activeJobId)?.operationId;
|
|
2473
|
+
}
|
|
2474
|
+
/** Routes are keyed by connectId; a job key may be the bare wallet id. */
|
|
2475
|
+
_operationRouteForIdentifier(identifier) {
|
|
2476
|
+
for (const key of keystoneCancelQueueKeys([identifier])) {
|
|
2477
|
+
const route = this._operationRoutes.get(key);
|
|
2478
|
+
if (route) return route;
|
|
2479
|
+
}
|
|
2480
|
+
const walletId = keystoneWalletIdFromIdentifier(identifier);
|
|
2481
|
+
return walletId ? this._operationRoutes.get(walletConnectId(walletId)) : void 0;
|
|
2482
|
+
}
|
|
2483
|
+
/**
|
|
2484
|
+
* A QR prompt has no transport to interrupt, so an aborted job would sit here
|
|
2485
|
+
* until the QR timeout. Releasing the registry slot on abort is what lets a
|
|
2486
|
+
* cancel reach the wait at all.
|
|
2487
|
+
*/
|
|
2488
|
+
async _awaitQrResponse(requestType, pending, signal) {
|
|
2489
|
+
if (!signal) return pending;
|
|
2490
|
+
let settled = false;
|
|
2491
|
+
const onAbort = () => {
|
|
2492
|
+
if (!settled) this._uiRegistry.cancel(requestType);
|
|
2493
|
+
};
|
|
2494
|
+
if (signal.aborted) onAbort();
|
|
2495
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
2496
|
+
try {
|
|
2497
|
+
return await pending;
|
|
2498
|
+
} finally {
|
|
2499
|
+
settled = true;
|
|
2500
|
+
signal.removeEventListener("abort", onAbort);
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
async _requestQrDisplayAndAwaitResponse(device, data, signal) {
|
|
2504
|
+
const operationId = this._activeOperationId();
|
|
2385
2505
|
const waitPromise = this._uiRegistry.wait(
|
|
2386
2506
|
UI_REQUEST.REQUEST_QR_DISPLAY,
|
|
2387
|
-
{ timeoutMs: this._qrTimeoutMs }
|
|
2507
|
+
{ timeoutMs: this._qrTimeoutMs, operationId }
|
|
2388
2508
|
);
|
|
2389
2509
|
this.emitter.emit(UI_REQUEST.REQUEST_QR_DISPLAY, {
|
|
2390
2510
|
type: UI_REQUEST.REQUEST_QR_DISPLAY,
|
|
2391
|
-
payload: { device, data }
|
|
2511
|
+
payload: { device, data, operationId }
|
|
2392
2512
|
});
|
|
2393
|
-
const response = await
|
|
2513
|
+
const response = await this._awaitQrResponse(
|
|
2514
|
+
UI_REQUEST.REQUEST_QR_DISPLAY,
|
|
2515
|
+
waitPromise,
|
|
2516
|
+
signal
|
|
2517
|
+
);
|
|
2394
2518
|
return { urType: response.urType, urData: response.urData };
|
|
2395
2519
|
}
|
|
2396
|
-
async _requestQrScanAndAwaitResponse(device) {
|
|
2520
|
+
async _requestQrScanAndAwaitResponse(device, signal) {
|
|
2521
|
+
const operationId = this._activeOperationId();
|
|
2397
2522
|
const waitPromise = this._uiRegistry.wait(
|
|
2398
2523
|
UI_REQUEST.REQUEST_QR_SCAN,
|
|
2399
|
-
{ timeoutMs: this._qrTimeoutMs }
|
|
2524
|
+
{ timeoutMs: this._qrTimeoutMs, operationId }
|
|
2400
2525
|
);
|
|
2401
2526
|
this.emitter.emit(UI_REQUEST.REQUEST_QR_SCAN, {
|
|
2402
2527
|
type: UI_REQUEST.REQUEST_QR_SCAN,
|
|
2403
|
-
payload: { device }
|
|
2528
|
+
payload: { device, operationId }
|
|
2404
2529
|
});
|
|
2405
|
-
const response = await waitPromise;
|
|
2530
|
+
const response = await this._awaitQrResponse(UI_REQUEST.REQUEST_QR_SCAN, waitPromise, signal);
|
|
2406
2531
|
return { urType: response.urType, urData: response.urData };
|
|
2407
2532
|
}
|
|
2408
2533
|
async _callUsbConnector(sessionId, method, params, signal) {
|
|
@@ -2418,6 +2543,12 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2418
2543
|
message: `Keystone USB is busy while calling ${method}`
|
|
2419
2544
|
});
|
|
2420
2545
|
}
|
|
2546
|
+
if (signal?.aborted) {
|
|
2547
|
+
throw signal.reason instanceof Error ? signal.reason : createHwkError({
|
|
2548
|
+
code: HardwareErrorCode.UserAborted,
|
|
2549
|
+
message: "User aborted operation"
|
|
2550
|
+
});
|
|
2551
|
+
}
|
|
2421
2552
|
const releaseOperation = this._retainUsbOperation(`call:${sessionId}`);
|
|
2422
2553
|
let rawCall;
|
|
2423
2554
|
try {
|
|
@@ -2516,7 +2647,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2516
2647
|
_errorToFailure(err) {
|
|
2517
2648
|
if (err && typeof err === "object") {
|
|
2518
2649
|
const e = err;
|
|
2519
|
-
if (typeof e.code === "number") {
|
|
2650
|
+
if (typeof e.code === "number" && e.code >= 1e4 && e.code <= 99999) {
|
|
2520
2651
|
const { origin } = e;
|
|
2521
2652
|
return failure(
|
|
2522
2653
|
e.code,
|