@onekeyfe/hwk-keystone-adapter 1.2.3-alpha.4 → 1.2.5-alpha.0
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 +97 -214
- package/dist/index.d.ts +97 -214
- package/dist/index.js +852 -977
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +692 -815
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -67,6 +67,7 @@ var TronSignRequest = class _TronSignRequest extends import_bc_ur_registry.Regis
|
|
|
67
67
|
this.getSignData = () => this.signData;
|
|
68
68
|
this.getSignType = () => this.signType;
|
|
69
69
|
this.getDerivationPath = () => this.derivationPath.getPath();
|
|
70
|
+
this.getOrigin = () => this.origin;
|
|
70
71
|
this.toDataItem = () => {
|
|
71
72
|
const map = {};
|
|
72
73
|
if (this.requestId) {
|
|
@@ -139,6 +140,9 @@ var TronSignature = class _TronSignature extends import_bc_ur_registry2.Registry
|
|
|
139
140
|
static fromDataItem(dataItem) {
|
|
140
141
|
const map = dataItem.getData();
|
|
141
142
|
const signature = map[2 /* signature */];
|
|
143
|
+
if (!Buffer.isBuffer(signature)) {
|
|
144
|
+
throw new Error("Keystone tron-signature UR is missing its signature bytes");
|
|
145
|
+
}
|
|
142
146
|
const requestIdItem = map[1 /* requestId */];
|
|
143
147
|
const requestId = requestIdItem?.getData();
|
|
144
148
|
return new _TronSignature(signature, requestId);
|
|
@@ -169,14 +173,22 @@ function toSdkUr(ur) {
|
|
|
169
173
|
function fromSdkUr(ur) {
|
|
170
174
|
return { urType: ur.type, urData: ur.cbor.toString("hex") };
|
|
171
175
|
}
|
|
172
|
-
function
|
|
173
|
-
if (hex.length !==
|
|
176
|
+
function splitEvmSignature(hex) {
|
|
177
|
+
if (hex.length < 130 || hex.length % 2 !== 0) {
|
|
174
178
|
throw new Error(
|
|
175
|
-
`Keystone returned a ${hex.length / 2}-byte signature; expected 65 bytes (r|s|v)`
|
|
179
|
+
`Keystone returned a ${hex.length / 2}-byte signature; expected at least 65 bytes (r|s|v)`
|
|
176
180
|
);
|
|
177
181
|
}
|
|
178
182
|
return { r: hex.slice(0, 64), s: hex.slice(64, 128), v: hex.slice(128) };
|
|
179
183
|
}
|
|
184
|
+
function requireSignatureBytes(hex, expectedBytes, chain) {
|
|
185
|
+
if (hex.length !== expectedBytes * 2) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Keystone returned a ${hex.length / 2}-byte ${chain} signature; expected ${expectedBytes} bytes`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return hex;
|
|
191
|
+
}
|
|
180
192
|
function requireBip32MasterFingerprint(value) {
|
|
181
193
|
const fingerprint = (0, import_hwk_adapter_core.parseBip32MasterFingerprint)(value);
|
|
182
194
|
if (!fingerprint) {
|
|
@@ -184,12 +196,24 @@ function requireBip32MasterFingerprint(value) {
|
|
|
184
196
|
}
|
|
185
197
|
return fingerprint;
|
|
186
198
|
}
|
|
199
|
+
function toParsedAccount(key) {
|
|
200
|
+
return {
|
|
201
|
+
chain: key.chain,
|
|
202
|
+
path: key.path,
|
|
203
|
+
publicKey: key.publicKey,
|
|
204
|
+
extendedPublicKey: key.extendedPublicKey,
|
|
205
|
+
xfp: key.xfp ? requireBip32MasterFingerprint(key.xfp) : void 0,
|
|
206
|
+
name: key.name
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function relativeHdPath(relativeDerivePath) {
|
|
210
|
+
return `m/${relativeDerivePath.replace(/^m\//i, "")}`;
|
|
211
|
+
}
|
|
187
212
|
var KeystoneUrEngine = class {
|
|
188
213
|
constructor(origin = "OneKey") {
|
|
189
214
|
this.sdk = new import_keystone_sdk.KeystoneSDK({ origin });
|
|
190
215
|
}
|
|
191
|
-
// --- Account sync (
|
|
192
|
-
// wallet-initiated KeyDerivation request/response over either channel) ---
|
|
216
|
+
// --- Account sync (device-initiated export or KeyDerivation response) ---
|
|
193
217
|
parseMultiAccounts(ur) {
|
|
194
218
|
const parsed = this.sdk.parseMultiAccounts(toSdkUr(ur));
|
|
195
219
|
return {
|
|
@@ -197,46 +221,15 @@ var KeystoneUrEngine = class {
|
|
|
197
221
|
device: parsed.device,
|
|
198
222
|
deviceId: parsed.deviceId,
|
|
199
223
|
deviceVersion: parsed.deviceVersion,
|
|
200
|
-
accounts: parsed.keys.map(
|
|
201
|
-
(key) => ({
|
|
202
|
-
chain: key.chain,
|
|
203
|
-
path: key.path,
|
|
204
|
-
publicKey: key.publicKey,
|
|
205
|
-
extendedPublicKey: key.extendedPublicKey,
|
|
206
|
-
xfp: key.xfp ? requireBip32MasterFingerprint(key.xfp) : void 0,
|
|
207
|
-
name: key.name
|
|
208
|
-
})
|
|
209
|
-
)
|
|
224
|
+
accounts: parsed.keys.map(toParsedAccount)
|
|
210
225
|
};
|
|
211
226
|
}
|
|
212
227
|
parseHDKey(ur) {
|
|
213
|
-
|
|
214
|
-
return {
|
|
215
|
-
chain: key.chain,
|
|
216
|
-
path: key.path,
|
|
217
|
-
publicKey: key.publicKey,
|
|
218
|
-
extendedPublicKey: key.extendedPublicKey,
|
|
219
|
-
xfp: key.xfp ? requireBip32MasterFingerprint(key.xfp) : void 0,
|
|
220
|
-
name: key.name
|
|
221
|
-
};
|
|
228
|
+
return toParsedAccount(this.sdk.parseHDKey(toSdkUr(ur)));
|
|
222
229
|
}
|
|
223
230
|
/**
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* showing. The device replies with a `crypto-multi-accounts` UR — parse it
|
|
227
|
-
* with `parseMultiAccounts`. Used for the implicit "sync this wallet's xfp
|
|
228
|
-
* before the first sign" round trip as well as an explicit account import.
|
|
229
|
-
*
|
|
230
|
-
* `version: V1` is required — verified against real Keystone hardware and
|
|
231
|
-
* `keystone3-firmware`'s `CheckHardwareCallRequestIsLegal` source: an
|
|
232
|
-
* unversioned/V0 request is validated as a legacy Cardano-only request
|
|
233
|
-
* (`m/1852'/1815'/...`) and firmware rejects every other chain's path with
|
|
234
|
-
* `PRS_PARSING_ERROR` (device-shown message: "路径不受支持" / "path not
|
|
235
|
-
* supported"), regardless of the path's shape. V1 is what actually enables
|
|
236
|
-
* the general per-chain path whitelist (includes `m/44'/60'` for ETH, the
|
|
237
|
-
* standard BTC purposes, etc.). The SDK itself defaults to V0 unless a
|
|
238
|
-
* truthy `version` is passed — omitting this silently produces a request
|
|
239
|
-
* every non-Cardano device rejects.
|
|
231
|
+
* Builds a KeyDerivation request. `version: V1` is required: firmware validates V0 (the SDK
|
|
232
|
+
* default) as Cardano-only and rejects other paths with `PRS_PARSING_ERROR`.
|
|
240
233
|
*/
|
|
241
234
|
buildKeyDerivationRequest(input) {
|
|
242
235
|
const ur = this.sdk.generateKeyDerivationCall({
|
|
@@ -250,14 +243,8 @@ var KeystoneUrEngine = class {
|
|
|
250
243
|
return fromSdkUr(ur);
|
|
251
244
|
}
|
|
252
245
|
/**
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
* `crypto-hdkey` for a single-key response some firmware paths use instead.
|
|
256
|
-
* Both are normalized to the same `KeystoneParsedMultiAccounts` shape — a
|
|
257
|
-
* single `crypto-hdkey` becomes a one-entry account list, with its own
|
|
258
|
-
* `origin.sourceFingerprint` promoted to `masterFingerprint` (correct for a
|
|
259
|
-
* key derived directly from the seed, which every request this engine
|
|
260
|
-
* builds asks for).
|
|
246
|
+
* Normalizes `crypto-multi-accounts` and the single-key `crypto-hdkey` some firmware returns. The
|
|
247
|
+
* hdkey source fingerprint is the mfp only because every request here derives from the seed.
|
|
261
248
|
*/
|
|
262
249
|
parseAccountResponse(ur) {
|
|
263
250
|
if (ur.urType === "crypto-hdkey") {
|
|
@@ -288,37 +275,19 @@ var KeystoneUrEngine = class {
|
|
|
288
275
|
}
|
|
289
276
|
parseEthSignature(ur) {
|
|
290
277
|
const signature = this.sdk.eth.parseSignature(toSdkUr(ur));
|
|
291
|
-
return { requestId: signature.requestId, ...
|
|
278
|
+
return { requestId: signature.requestId, ...splitEvmSignature(signature.signature) };
|
|
292
279
|
}
|
|
293
|
-
/**
|
|
294
|
-
* Derive one EVM address offline from an already-synced account xpub —
|
|
295
|
-
* verified against the same `@keystonehq/bc-ur-registry-eth` helper the
|
|
296
|
-
* Keystone-based OneKey air-gap demo uses in production
|
|
297
|
-
* (`generateAddressFromXpub`), so no unverified assumption about what a
|
|
298
|
-
* leaf-path KeyDerivation request would return. `relativeDerivePath` is
|
|
299
|
-
* relative to the xpub's own depth, e.g. `'0/0'` for an account xpub.
|
|
300
|
-
*/
|
|
280
|
+
/** Derives an EVM address offline; `relativeDerivePath` is relative to the xpub, e.g. `'0/0'`. */
|
|
301
281
|
deriveEvmAddressFromXpub(xpub, relativeDerivePath) {
|
|
302
|
-
return (0, import_bc_ur_registry_eth.generateAddressFromXpub)(xpub,
|
|
282
|
+
return (0, import_bc_ur_registry_eth.generateAddressFromXpub)(xpub, relativeHdPath(relativeDerivePath));
|
|
303
283
|
}
|
|
304
284
|
// --- BTC (PSBT transaction signing + plain message signing) ---
|
|
305
285
|
/**
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
* always emits standard mainnet-xpub version bytes (`0488B21E`) regardless
|
|
309
|
-
* of the account's purpose/script type (verified against
|
|
310
|
-
* `@keystonehq/bc-ur-registry`'s `CryptoHDKey.getBip32Key`, which hardcodes
|
|
311
|
-
* that version rather than switching to a SLIP-132 ypub/zpub prefix per
|
|
312
|
-
* script type) — so `hdkey.fromExtendedKey` parses it correctly for every
|
|
313
|
-
* `scriptType` without needing custom version bytes configured.
|
|
314
|
-
*
|
|
315
|
-
* `p2tr` is deliberately not handled: taproot output-key tweaking (BIP-341)
|
|
316
|
-
* needs an elliptic-curve library wired via bitcoinjs-lib's `initEccLib`,
|
|
317
|
-
* which this package doesn't set up yet — every other payment function
|
|
318
|
-
* here needs no such library.
|
|
286
|
+
* `CryptoHDKey` always emits mainnet xpub version bytes (`0488B21E`), so `hdkey` parses it as is.
|
|
287
|
+
* `p2tr` needs an ECC lib for BIP-341 tweaking, not wired here.
|
|
319
288
|
*/
|
|
320
289
|
deriveBtcAddressFromXpub(xpub, relativeDerivePath, scriptType) {
|
|
321
|
-
const node = import_hdkey.default.fromExtendedKey(xpub).derive(
|
|
290
|
+
const node = import_hdkey.default.fromExtendedKey(xpub).derive(relativeHdPath(relativeDerivePath));
|
|
322
291
|
if (!node.publicKey) throw new Error("HDKey derivation did not produce a public key");
|
|
323
292
|
return this.deriveBtcAddressFromPublicKey(
|
|
324
293
|
Buffer.from(node.publicKey).toString("hex"),
|
|
@@ -371,7 +340,7 @@ var KeystoneUrEngine = class {
|
|
|
371
340
|
requestId: params.requestId,
|
|
372
341
|
signData: params.messageHex,
|
|
373
342
|
dataType: 1,
|
|
374
|
-
// BtcSignRequest.DataType.message
|
|
343
|
+
// BtcSignRequest.DataType.message; PSBT signing never goes through this path.
|
|
375
344
|
accounts: params.accounts,
|
|
376
345
|
origin: params.origin
|
|
377
346
|
});
|
|
@@ -400,7 +369,10 @@ var KeystoneUrEngine = class {
|
|
|
400
369
|
}
|
|
401
370
|
parseSolSignature(ur) {
|
|
402
371
|
const signature = this.sdk.sol.parseSignature(toSdkUr(ur));
|
|
403
|
-
return {
|
|
372
|
+
return {
|
|
373
|
+
requestId: signature.requestId,
|
|
374
|
+
signature: requireSignatureBytes(signature.signature, 64, "SOL")
|
|
375
|
+
};
|
|
404
376
|
}
|
|
405
377
|
// --- TRON ---
|
|
406
378
|
/**
|
|
@@ -425,37 +397,22 @@ var KeystoneUrEngine = class {
|
|
|
425
397
|
const requestId = signature.getRequestId();
|
|
426
398
|
return {
|
|
427
399
|
requestId: requestId ? (0, import_uuid.stringify)(requestId) : void 0,
|
|
428
|
-
signature: signature.getSignature().toString("hex")
|
|
400
|
+
signature: requireSignatureBytes(signature.getSignature().toString("hex"), 65, "TRON")
|
|
429
401
|
};
|
|
430
402
|
}
|
|
431
403
|
/**
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
* derivation (verified against Keystone's own `formatAddress()` in
|
|
435
|
-
* `keystone-sdk`'s TRON chain source) — only the final text encoding
|
|
436
|
-
* differs (base58check with a `0x41` version byte, not checksummed hex).
|
|
437
|
-
* Reusing `generateAddressFromXpub` here means no new hashing dependency:
|
|
438
|
-
* strip its "0x" and re-encode the same 20 bytes.
|
|
404
|
+
* TRON uses the same 20 address bytes as EVM (Keystone's `formatAddress()`), re-encoded as
|
|
405
|
+
* base58check with the `0x41` version byte.
|
|
439
406
|
*/
|
|
440
407
|
deriveTronAddressFromXpub(xpub, relativeDerivePath) {
|
|
441
|
-
const evmStyleHex = (0, import_bc_ur_registry_eth.generateAddressFromXpub)(
|
|
442
|
-
xpub,
|
|
443
|
-
`m/${relativeDerivePath.replace(/^m\//i, "")}`
|
|
444
|
-
);
|
|
408
|
+
const evmStyleHex = (0, import_bc_ur_registry_eth.generateAddressFromXpub)(xpub, relativeHdPath(relativeDerivePath));
|
|
445
409
|
const addressBytes = Buffer.concat([
|
|
446
410
|
Buffer.from([TRON_ADDRESS_PREFIX]),
|
|
447
|
-
Buffer.from(evmStyleHex
|
|
411
|
+
Buffer.from(stripHexPrefix(evmStyleHex), "hex")
|
|
448
412
|
]);
|
|
449
413
|
return import_bs58check.default.encode(addressBytes);
|
|
450
414
|
}
|
|
451
|
-
/**
|
|
452
|
-
* Split an account-level xpub back into the BIP-32 fields a host needs to
|
|
453
|
-
* treat it as a real extended key (`BtcPublicKey`). Everything here is
|
|
454
|
-
* carried inside the xpub's own serialization — depth, the PARENT key
|
|
455
|
-
* fingerprint (not the seed's master fingerprint), the chain code and the
|
|
456
|
-
* compressed public key — so this is a pure decode with no device round
|
|
457
|
-
* trip; the xpub itself was already device-verified when it was synced.
|
|
458
|
-
*/
|
|
415
|
+
/** Decodes an xpub's BIP-32 fields; `parentFingerprint` is the parent key's, not the mfp. */
|
|
459
416
|
parseXpubMeta(xpub) {
|
|
460
417
|
const raw = Buffer.from(import_bs58check.default.decode(xpub));
|
|
461
418
|
if (raw.length !== 78) {
|
|
@@ -473,7 +430,7 @@ var KeystoneUrEngine = class {
|
|
|
473
430
|
// src/adapter/KeystoneAdapter.ts
|
|
474
431
|
var import_bs58 = __toESM(require("bs58"));
|
|
475
432
|
var import_uuid2 = require("uuid");
|
|
476
|
-
var
|
|
433
|
+
var import_hwk_adapter_core4 = require("@onekeyfe/hwk-adapter-core");
|
|
477
434
|
|
|
478
435
|
// src/adapter/deviceTable.ts
|
|
479
436
|
var import_hwk_adapter_core2 = require("@onekeyfe/hwk-adapter-core");
|
|
@@ -584,21 +541,47 @@ function placeholderDeviceInfo() {
|
|
|
584
541
|
};
|
|
585
542
|
}
|
|
586
543
|
|
|
544
|
+
// src/utils/queueKey.ts
|
|
545
|
+
var import_hwk_adapter_core3 = require("@onekeyfe/hwk-adapter-core");
|
|
546
|
+
var KEYSTONE_COLD_START_JOB_LABEL = "keystone-cold-start";
|
|
547
|
+
function keystoneQueueKey(connectId, deviceId) {
|
|
548
|
+
return (0, import_hwk_adapter_core3.isHardwareOperationId)(connectId) ? connectId : deviceId || connectId || KEYSTONE_COLD_START_JOB_LABEL;
|
|
549
|
+
}
|
|
550
|
+
function keystoneWalletIdFromIdentifier(identifier) {
|
|
551
|
+
const value = identifier.startsWith(KEYSTONE_WALLET_CONNECT_ID_PREFIX) ? identifier.slice(KEYSTONE_WALLET_CONNECT_ID_PREFIX.length) : identifier;
|
|
552
|
+
return /^[0-9a-f]{64}$/i.test(value) ? value.toLowerCase() : void 0;
|
|
553
|
+
}
|
|
554
|
+
function keystoneCancelQueueKeys(identifiers) {
|
|
555
|
+
const keys = /* @__PURE__ */ new Set();
|
|
556
|
+
for (const identifier of identifiers) {
|
|
557
|
+
if (identifier) {
|
|
558
|
+
keys.add(identifier);
|
|
559
|
+
const walletId = keystoneWalletIdFromIdentifier(identifier);
|
|
560
|
+
if (walletId) keys.add(walletId);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return keys;
|
|
564
|
+
}
|
|
565
|
+
|
|
587
566
|
// src/adapter/KeystoneAdapter.ts
|
|
588
|
-
var COLD_START_JOB_LABEL = "keystone-cold-start";
|
|
589
567
|
var KEYSTONE_USB_REATTACH_PROBE_ATTEMPTS = 4;
|
|
590
568
|
var KEYSTONE_USB_REATTACH_PROBE_INTERVAL_MS = 500;
|
|
591
569
|
function resolveHardwareOperationTarget(positionalTargetId, operationId) {
|
|
592
|
-
|
|
593
|
-
|
|
570
|
+
return (0, import_hwk_adapter_core4.resolveHardwareOperationTarget)(positionalTargetId, operationId, "keystone");
|
|
571
|
+
}
|
|
572
|
+
function keySchema(chain, path) {
|
|
573
|
+
return { path, curve: chain === "sol" ? "ed25519" : "secp256k1" };
|
|
594
574
|
}
|
|
595
575
|
function waitForKeystoneUsbReattachProbe() {
|
|
596
576
|
return new Promise((resolve) => {
|
|
597
577
|
setTimeout(resolve, KEYSTONE_USB_REATTACH_PROBE_INTERVAL_MS);
|
|
598
578
|
});
|
|
599
579
|
}
|
|
600
|
-
function
|
|
601
|
-
|
|
580
|
+
function buildCancelledFailure(signal) {
|
|
581
|
+
const reason = signal.reason;
|
|
582
|
+
const code = typeof reason?.code === "number" ? reason.code : import_hwk_adapter_core4.HardwareErrorCode.UserAborted;
|
|
583
|
+
const message = typeof reason?.message === "string" ? reason.message : "";
|
|
584
|
+
return (0, import_hwk_adapter_core4.failure)(code, message || "All-network get-address cancelled");
|
|
602
585
|
}
|
|
603
586
|
var BIP44_COIN_TYPE_TO_CHAIN = {
|
|
604
587
|
60: "evm",
|
|
@@ -613,17 +596,20 @@ function inferHwkChainFromPath(path) {
|
|
|
613
596
|
var KeystoneAdapter = class _KeystoneAdapter {
|
|
614
597
|
constructor(options) {
|
|
615
598
|
this.vendor = "keystone";
|
|
616
|
-
|
|
599
|
+
// No protocol-level cancel exists over Keystone USB, and QR has no transport
|
|
600
|
+
// to interrupt at all. Cancelling ends the wait, not the device's prompt.
|
|
601
|
+
this.cancelCapability = "stops-waiting";
|
|
602
|
+
this.emitter = new import_hwk_adapter_core4.TypedEventEmitter();
|
|
617
603
|
this._operationRoutes = /* @__PURE__ */ new Map();
|
|
618
|
-
this._operations = new
|
|
604
|
+
this._operations = new import_hwk_adapter_core4.OperationRegistry({
|
|
619
605
|
vendor: "keystone",
|
|
620
606
|
onEnded: (operation, reason) => {
|
|
621
607
|
const route = this._operationRoutes.get(operation.connectId);
|
|
622
608
|
if (route?.operationId === operation.operationId) {
|
|
623
609
|
this._operationRoutes.delete(operation.connectId);
|
|
624
610
|
}
|
|
625
|
-
this.emitter.emit(
|
|
626
|
-
type:
|
|
611
|
+
this.emitter.emit(import_hwk_adapter_core4.SDK.OPERATION_ENDED, {
|
|
612
|
+
type: import_hwk_adapter_core4.SDK.OPERATION_ENDED,
|
|
627
613
|
payload: { operationId: operation.operationId, reason }
|
|
628
614
|
});
|
|
629
615
|
if (reason === "timeout") {
|
|
@@ -631,54 +617,62 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
631
617
|
}
|
|
632
618
|
}
|
|
633
619
|
});
|
|
634
|
-
this._uiRegistry = new
|
|
620
|
+
this._uiRegistry = new import_hwk_adapter_core4.UiRequestRegistry();
|
|
635
621
|
this._devices = /* @__PURE__ */ new Map();
|
|
636
622
|
this._searchDeviceTargets = /* @__PURE__ */ new Map();
|
|
637
623
|
/** Serializes USB open/identity handshakes that run outside the job queue. */
|
|
638
624
|
this._usbConnectTail = Promise.resolve();
|
|
639
625
|
this._unsettledUsbOperations = /* @__PURE__ */ new Map();
|
|
640
626
|
this._usbIdleWaiters = /* @__PURE__ */ new Set();
|
|
627
|
+
this._abandonedUsbConnects = 0;
|
|
641
628
|
this._usbTeardownTail = Promise.resolve();
|
|
642
629
|
this._pendingUsbTeardowns = 0;
|
|
643
630
|
this._handleUsbDisconnect = ({ connectId }) => {
|
|
644
631
|
const record = Array.from(this._devices.values()).find((item) => item.usbSessionId === connectId);
|
|
645
632
|
if (!record) return;
|
|
646
633
|
this._operations.endByConnectionKey(connectId, "disconnect");
|
|
647
|
-
this._operations.endByConnectionKey(record.connectId, "disconnect");
|
|
648
634
|
record.usbSessionId = void 0;
|
|
649
635
|
const info = toDeviceInfo(record);
|
|
650
636
|
if (record.qrSynced) {
|
|
651
|
-
this.emitter.emit(
|
|
637
|
+
this.emitter.emit(import_hwk_adapter_core4.DEVICE.CHANGED, { type: import_hwk_adapter_core4.DEVICE.CHANGED, payload: info });
|
|
652
638
|
} else {
|
|
653
639
|
this._devices.delete(record.walletId);
|
|
654
|
-
this.emitter.emit(
|
|
640
|
+
this.emitter.emit(import_hwk_adapter_core4.DEVICE.DISCONNECT, { type: import_hwk_adapter_core4.DEVICE.DISCONNECT, payload: info });
|
|
655
641
|
}
|
|
656
642
|
};
|
|
657
643
|
this._handleUsbUiEvent = (event) => {
|
|
658
644
|
this.emitter.emit("ui-event", event);
|
|
659
645
|
};
|
|
660
646
|
// ---------------------------------------------------------------------------
|
|
661
|
-
// All-network bundle
|
|
662
|
-
// there's exactly one implementation of each chain's address logic.
|
|
647
|
+
// All-network bundle, dispatched to the per-chain methods below.
|
|
663
648
|
// ---------------------------------------------------------------------------
|
|
664
649
|
this.allNetworkGetAddress = async (connectId, deviceId, params) => {
|
|
665
650
|
const operationTarget = resolveHardwareOperationTarget(connectId, params.operationId);
|
|
666
651
|
if (!operationTarget.success) return operationTarget;
|
|
667
652
|
const effectiveConnectId = operationTarget.payload.targetId ?? "";
|
|
668
653
|
const { operationId } = operationTarget.payload;
|
|
654
|
+
let cancelScope;
|
|
669
655
|
try {
|
|
670
|
-
const prefetched = await this._prefetchAllNetworkAccounts(
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
);
|
|
656
|
+
const prefetched = await this._prefetchAllNetworkAccounts(effectiveConnectId, deviceId, {
|
|
657
|
+
...params,
|
|
658
|
+
operationId
|
|
659
|
+
});
|
|
675
660
|
const { book } = prefetched;
|
|
676
661
|
const itemDeviceId = deviceId || prefetched.walletId || "";
|
|
677
|
-
|
|
662
|
+
const scope = this._jobQueue.createCancelScope(
|
|
663
|
+
keystoneQueueKey(effectiveConnectId, itemDeviceId)
|
|
664
|
+
);
|
|
665
|
+
cancelScope = scope;
|
|
666
|
+
const cancelledIndexes = /* @__PURE__ */ new Set();
|
|
667
|
+
return await (0, import_hwk_adapter_core4.runAllNetworkGetAddress)({
|
|
678
668
|
connectId: effectiveConnectId,
|
|
679
669
|
deviceId: itemDeviceId,
|
|
680
670
|
params,
|
|
681
|
-
callItem: async ({ method, item }) => {
|
|
671
|
+
callItem: async ({ method, item, index }) => {
|
|
672
|
+
if (scope.signal.aborted) {
|
|
673
|
+
cancelledIndexes.add(index);
|
|
674
|
+
return buildCancelledFailure(scope.signal);
|
|
675
|
+
}
|
|
682
676
|
const commonArgs = {
|
|
683
677
|
path: item.path,
|
|
684
678
|
showOnDevice: item.showOnDevice,
|
|
@@ -696,8 +690,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
696
690
|
case "tronGetAddress":
|
|
697
691
|
return this.tronGetAddress(effectiveConnectId, itemDeviceId, commonArgs, book);
|
|
698
692
|
default:
|
|
699
|
-
return (0,
|
|
700
|
-
|
|
693
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
694
|
+
import_hwk_adapter_core4.HardwareErrorCode.MethodNotSupported,
|
|
701
695
|
`Unsupported method: ${String(method)}`
|
|
702
696
|
);
|
|
703
697
|
}
|
|
@@ -717,16 +711,24 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
717
711
|
} : void 0
|
|
718
712
|
}
|
|
719
713
|
});
|
|
714
|
+
},
|
|
715
|
+
// Cancel, refusal or a lost session ends the batch; other per-item
|
|
716
|
+
// failures carry on with the next chain.
|
|
717
|
+
shouldAbortBundle: (response, { index }) => {
|
|
718
|
+
const { code } = response.payload ?? {};
|
|
719
|
+
return cancelledIndexes.has(index) || (0, import_hwk_adapter_core4.isUserRefusal)(code) || (0, import_hwk_adapter_core4.isConnectionLost)(code);
|
|
720
720
|
}
|
|
721
721
|
});
|
|
722
722
|
} catch (err) {
|
|
723
723
|
return this._errorToFailure(err);
|
|
724
|
+
} finally {
|
|
725
|
+
cancelScope?.release();
|
|
724
726
|
}
|
|
725
727
|
};
|
|
726
728
|
this._origin = options?.origin ?? "OneKey";
|
|
727
729
|
this._qrTimeoutMs = options?.qrTimeoutMs;
|
|
728
730
|
this.urEngine = new KeystoneUrEngine(this._origin);
|
|
729
|
-
this._jobQueue = new
|
|
731
|
+
this._jobQueue = new import_hwk_adapter_core4.DeviceJobQueue();
|
|
730
732
|
this._usbConnector = options?.usbConnector;
|
|
731
733
|
this._usbConnector?.on("device-disconnect", this._handleUsbDisconnect);
|
|
732
734
|
this._usbConnector?.on("ui-event", this._handleUsbUiEvent);
|
|
@@ -742,7 +744,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
742
744
|
getAvailableTransports() {
|
|
743
745
|
return this._usbConnector ? ["qr", "usb"] : ["qr"];
|
|
744
746
|
}
|
|
745
|
-
/** Pins
|
|
747
|
+
/** Pins calls to 'qr' or 'usb'; any other value clears the pin back to auto routing. */
|
|
746
748
|
switchTransport(type) {
|
|
747
749
|
this._forcedTransport = type === "qr" || type === "usb" ? type : void 0;
|
|
748
750
|
return Promise.resolve();
|
|
@@ -755,6 +757,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
755
757
|
this._uiRegistry.cancel();
|
|
756
758
|
this._jobQueue.clear();
|
|
757
759
|
this._searchDeviceTargets.clear();
|
|
760
|
+
await this._usbConnectTail;
|
|
758
761
|
await this._resetUsbSessions();
|
|
759
762
|
this._usbConnector?.off("device-disconnect", this._handleUsbDisconnect);
|
|
760
763
|
this._usbConnector?.off("ui-event", this._handleUsbUiEvent);
|
|
@@ -766,14 +769,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
766
769
|
// Device table
|
|
767
770
|
// ---------------------------------------------------------------------------
|
|
768
771
|
/**
|
|
769
|
-
*
|
|
770
|
-
*
|
|
771
|
-
* there is no physical descriptor to enumerate. When a USB connector is
|
|
772
|
-
* configured, its raw scan results are appended as-is: a USB descriptor has
|
|
773
|
-
* no mfp until `connectDevice()` actually opens+claims it (see
|
|
774
|
-
* `KeystoneUsbConnectorBase.searchDevices`), so these entries carry an
|
|
775
|
-
* empty `deviceId` and exist purely so a host can list "plugged in, click
|
|
776
|
-
* to connect" candidates.
|
|
772
|
+
* Returns known wallets plus USB scan results; USB entries have no mfp until `connectDevice()`
|
|
773
|
+
* claims them. Explicit QR discovery returns one virtual target.
|
|
777
774
|
*/
|
|
778
775
|
async searchDevices(options) {
|
|
779
776
|
const searchGeneration = /* @__PURE__ */ Symbol("keystone-device-search");
|
|
@@ -786,7 +783,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
786
783
|
}
|
|
787
784
|
const known = Array.from(this._devices.values()).map(toDeviceInfo);
|
|
788
785
|
if (options?.transportType === "qr") {
|
|
789
|
-
const searchTargetId = (0,
|
|
786
|
+
const searchTargetId = (0, import_hwk_adapter_core4.createHardwareSearchTargetId)("keystone");
|
|
790
787
|
this._searchDeviceTargets.set(searchTargetId, "qr");
|
|
791
788
|
return [
|
|
792
789
|
{
|
|
@@ -809,18 +806,15 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
809
806
|
if (this._activeSearchGeneration !== searchGeneration) return [];
|
|
810
807
|
const placeholders = usbDevices.map((d) => ({
|
|
811
808
|
vendor: "keystone",
|
|
812
|
-
// Empty, not 'unknown':
|
|
813
|
-
//
|
|
814
|
-
// string would beat the host's own default-name fallback and surface as
|
|
815
|
-
// a device called "unknown".
|
|
809
|
+
// Empty, not 'unknown': all models share one vid/pid, and a placeholder
|
|
810
|
+
// string would beat the host's default-name fallback.
|
|
816
811
|
model: d.model ?? "",
|
|
817
812
|
modelName: d.modelName,
|
|
818
|
-
//
|
|
813
|
+
// Unknown until the device is opened.
|
|
819
814
|
firmwareVersion: "0.0.0",
|
|
820
815
|
deviceId: d.deviceId,
|
|
821
816
|
connectId: d.connectId,
|
|
822
817
|
connectionType: "usb",
|
|
823
|
-
// The USB product name, which IS readable at enumeration time.
|
|
824
818
|
label: d.name,
|
|
825
819
|
serialNumber: d.serialNumber,
|
|
826
820
|
capabilities: d.capabilities ?? { persistentDeviceIdentity: false },
|
|
@@ -840,20 +834,25 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
840
834
|
Array.from(this._devices.values()).map((record) => record.usbSessionId).filter((sessionId) => Boolean(sessionId))
|
|
841
835
|
);
|
|
842
836
|
for (const sessionId of sessionIds) {
|
|
843
|
-
|
|
844
|
-
await this._usbConnector.disconnect(sessionId);
|
|
845
|
-
} catch {
|
|
846
|
-
}
|
|
847
|
-
this._handleUsbDisconnect({ connectId: sessionId });
|
|
837
|
+
await this._retireUsbSession(sessionId);
|
|
848
838
|
}
|
|
849
839
|
});
|
|
850
840
|
}
|
|
841
|
+
/** Best-effort disconnect; the session is retired either way so it cannot be selected again. */
|
|
842
|
+
async _retireUsbSession(sessionId) {
|
|
843
|
+
if (this._usbConnector) {
|
|
844
|
+
try {
|
|
845
|
+
await this._usbConnector.disconnect(sessionId);
|
|
846
|
+
} catch {
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
this._handleUsbDisconnect({ connectId: sessionId });
|
|
850
|
+
}
|
|
851
851
|
async searchDeviceTargets(options) {
|
|
852
852
|
const devices = await this.searchDevices(options);
|
|
853
853
|
return devices.map((device) => ({
|
|
854
854
|
searchTargetId: device.connectId,
|
|
855
|
-
//
|
|
856
|
-
// selectable handle remains owned by the current discovery snapshot.
|
|
855
|
+
// The wallet id is stable only after the USB/QR handshake.
|
|
857
856
|
searchTargetReusePolicy: "current-discovery",
|
|
858
857
|
vendor: device.vendor,
|
|
859
858
|
connectionType: device.connectionType,
|
|
@@ -872,25 +871,35 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
872
871
|
}));
|
|
873
872
|
}
|
|
874
873
|
async connectDevice(searchTargetId) {
|
|
874
|
+
const scope = this._jobQueue.createCancelScope(searchTargetId);
|
|
875
875
|
try {
|
|
876
876
|
let connected;
|
|
877
877
|
let selectedConnectionType;
|
|
878
878
|
const discoveredConnectionType = this._searchDeviceTargets.get(searchTargetId);
|
|
879
879
|
if (discoveredConnectionType === "qr") {
|
|
880
880
|
selectedConnectionType = "qr";
|
|
881
|
-
|
|
881
|
+
const abortColdStart = () => this._jobQueue.cancelActiveAndPending(
|
|
882
|
+
KEYSTONE_COLD_START_JOB_LABEL,
|
|
883
|
+
scope.signal.reason
|
|
884
|
+
);
|
|
885
|
+
scope.signal.addEventListener("abort", abortColdStart, { once: true });
|
|
886
|
+
try {
|
|
887
|
+
connected = await this.importFromQr();
|
|
888
|
+
} finally {
|
|
889
|
+
scope.signal.removeEventListener("abort", abortColdStart);
|
|
890
|
+
}
|
|
882
891
|
} else if (discoveredConnectionType === "usb") {
|
|
883
892
|
selectedConnectionType = "usb";
|
|
884
893
|
if (!this._usbConnector) {
|
|
885
|
-
return (0,
|
|
886
|
-
|
|
894
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
895
|
+
import_hwk_adapter_core4.HardwareErrorCode.TransportNotAvailable,
|
|
887
896
|
"No USB connector configured for this Keystone adapter"
|
|
888
897
|
);
|
|
889
898
|
}
|
|
890
|
-
connected = await this._connectUsb({}, searchTargetId);
|
|
891
|
-
} else if ((0,
|
|
892
|
-
return (0,
|
|
893
|
-
|
|
899
|
+
connected = await this._connectUsb({}, searchTargetId, scope.signal);
|
|
900
|
+
} else if ((0, import_hwk_adapter_core4.hasHardwareRuntimeIdPrefix)(searchTargetId)) {
|
|
901
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
902
|
+
import_hwk_adapter_core4.HardwareErrorCode.DeviceNotFound,
|
|
894
903
|
"Keystone search target has expired; search for devices again",
|
|
895
904
|
void 0,
|
|
896
905
|
void 0,
|
|
@@ -900,17 +909,18 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
900
909
|
const target = this._resolveTarget(searchTargetId);
|
|
901
910
|
if (target.record) {
|
|
902
911
|
selectedConnectionType = target.record.usbSessionId ? "usb" : "qr";
|
|
903
|
-
connected = (0,
|
|
912
|
+
connected = (0, import_hwk_adapter_core4.success)(toDeviceInfo(target.record));
|
|
904
913
|
} else if (!this._usbConnector) {
|
|
905
|
-
return (0,
|
|
906
|
-
|
|
914
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
915
|
+
import_hwk_adapter_core4.HardwareErrorCode.DeviceNotFound,
|
|
907
916
|
`Unknown Keystone device search target: ${searchTargetId}`
|
|
908
917
|
);
|
|
909
918
|
} else {
|
|
910
919
|
selectedConnectionType = "usb";
|
|
911
|
-
connected = await this._connectUsb(target);
|
|
920
|
+
connected = await this._connectUsb(target, void 0, scope.signal);
|
|
912
921
|
}
|
|
913
922
|
}
|
|
923
|
+
_KeystoneAdapter._throwIfAborted(scope.signal);
|
|
914
924
|
if (!connected.success) return connected;
|
|
915
925
|
const record = this._devices.get(connected.payload.deviceId);
|
|
916
926
|
this._operations.endByConnectionKey(connected.payload.connectId, "explicit");
|
|
@@ -918,23 +928,23 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
918
928
|
searchTargetId,
|
|
919
929
|
connectId: connected.payload.connectId,
|
|
920
930
|
device: connected.payload,
|
|
931
|
+
connectionType: selectedConnectionType,
|
|
921
932
|
connectionKeys: selectedConnectionType === "usb" && record?.usbSessionId ? [record.usbSessionId] : []
|
|
922
933
|
});
|
|
923
934
|
this._operationRoutes.set(connected.payload.connectId, {
|
|
924
935
|
operationId: operation.operationId,
|
|
925
936
|
connectionType: selectedConnectionType
|
|
926
937
|
});
|
|
927
|
-
return (0,
|
|
938
|
+
return (0, import_hwk_adapter_core4.success)(operation.operationId);
|
|
928
939
|
} catch (err) {
|
|
929
940
|
return this._errorToFailure(err);
|
|
941
|
+
} finally {
|
|
942
|
+
scope.release();
|
|
930
943
|
}
|
|
931
944
|
}
|
|
932
945
|
/**
|
|
933
|
-
* QR has
|
|
934
|
-
*
|
|
935
|
-
* this closes the connector session and either demotes the record back to
|
|
936
|
-
* QR-only (if it was ever QR-synced) or removes it entirely (pure-USB
|
|
937
|
-
* wallet that was never seen over QR) — see §4.2 of the design doc.
|
|
946
|
+
* QR has nothing to tear down. A USB session is closed, and the record is demoted to QR-only or
|
|
947
|
+
* removed if it was never QR-synced.
|
|
938
948
|
*/
|
|
939
949
|
async releaseOperation(operationId) {
|
|
940
950
|
const operation = this._operations.find(operationId);
|
|
@@ -958,53 +968,55 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
958
968
|
return;
|
|
959
969
|
}
|
|
960
970
|
const { usbSessionId } = record;
|
|
961
|
-
await this._runUsbTeardown(
|
|
962
|
-
if (this._usbConnector) {
|
|
963
|
-
try {
|
|
964
|
-
await this._usbConnector.disconnect(usbSessionId);
|
|
965
|
-
} catch {
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
this._handleUsbDisconnect({ connectId: usbSessionId });
|
|
969
|
-
});
|
|
971
|
+
await this._runUsbTeardown(() => this._retireUsbSession(usbSessionId));
|
|
970
972
|
}
|
|
971
973
|
getDeviceInfo(connectIdOrOperationId, deviceId) {
|
|
972
974
|
try {
|
|
973
|
-
const connectId = (0,
|
|
975
|
+
const connectId = (0, import_hwk_adapter_core4.isHardwareOperationId)(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
|
|
974
976
|
const { record } = this._resolveTarget(connectId, deviceId);
|
|
975
977
|
if (!record) {
|
|
976
978
|
return Promise.resolve(
|
|
977
|
-
(0,
|
|
978
|
-
|
|
979
|
+
(0, import_hwk_adapter_core4.failure)(
|
|
980
|
+
import_hwk_adapter_core4.HardwareErrorCode.DeviceNotFound,
|
|
979
981
|
`Unknown Keystone device: ${connectId || deviceId}`
|
|
980
982
|
)
|
|
981
983
|
);
|
|
982
984
|
}
|
|
983
|
-
return Promise.resolve((0,
|
|
985
|
+
return Promise.resolve((0, import_hwk_adapter_core4.success)(toDeviceInfo(record)));
|
|
984
986
|
} catch (err) {
|
|
985
987
|
return Promise.resolve(this._errorToFailure(err));
|
|
986
988
|
}
|
|
987
989
|
}
|
|
988
990
|
cancel(connectId) {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
+
let namedOperationConnectId;
|
|
992
|
+
if ((0, import_hwk_adapter_core4.isHardwareOperationId)(connectId)) {
|
|
993
|
+
try {
|
|
994
|
+
namedOperationConnectId = this._operations.resolve(connectId).connectId;
|
|
995
|
+
} catch {
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
const reason = (0, import_hwk_adapter_core4.createHwkError)({
|
|
1000
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.UserAborted,
|
|
991
1001
|
message: "User aborted operation"
|
|
992
1002
|
});
|
|
993
|
-
const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
|
|
994
1003
|
if (connectId) {
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1004
|
+
const queueKeys = keystoneCancelQueueKeys([
|
|
1005
|
+
connectId,
|
|
1006
|
+
this._operationRoutes.get(connectId)?.operationId,
|
|
1007
|
+
namedOperationConnectId
|
|
1008
|
+
]);
|
|
1009
|
+
for (const key of queueKeys) {
|
|
1010
|
+
this._jobQueue.cancelActiveAndPending(key, reason);
|
|
1011
|
+
}
|
|
1012
|
+
const scopedOperationId = (0, import_hwk_adapter_core4.isHardwareOperationId)(connectId) ? connectId : this._operationRouteForIdentifier(connectId)?.operationId;
|
|
1013
|
+
if (scopedOperationId) {
|
|
1014
|
+
this._uiRegistry.cancel(void 0, void 0, scopedOperationId);
|
|
1002
1015
|
}
|
|
1003
|
-
this._jobQueue.cancelActiveAndPending(activeJobId, reason);
|
|
1004
1016
|
} else {
|
|
1005
1017
|
this._jobQueue.cancelActiveAndPending(void 0, reason);
|
|
1018
|
+
this._uiRegistry.cancel();
|
|
1006
1019
|
}
|
|
1007
|
-
this._uiRegistry.cancel();
|
|
1008
1020
|
}
|
|
1009
1021
|
getChainFingerprint(connectId, deviceId, chain) {
|
|
1010
1022
|
try {
|
|
@@ -1012,16 +1024,16 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1012
1024
|
const walletId = target.record?.walletId ?? target.expectedWalletId;
|
|
1013
1025
|
if (!walletId) {
|
|
1014
1026
|
return Promise.resolve(
|
|
1015
|
-
(0,
|
|
1027
|
+
(0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.DeviceNotFound, "Unknown Keystone wallet identity")
|
|
1016
1028
|
);
|
|
1017
1029
|
}
|
|
1018
|
-
return Promise.resolve((0,
|
|
1030
|
+
return Promise.resolve((0, import_hwk_adapter_core4.success)((0, import_hwk_adapter_core4.deriveDeviceFingerprint)(`keystone:${chain}:${walletId}`)));
|
|
1019
1031
|
} catch (err) {
|
|
1020
1032
|
return Promise.resolve(this._errorToFailure(err));
|
|
1021
1033
|
}
|
|
1022
1034
|
}
|
|
1023
1035
|
uiResponse(response) {
|
|
1024
|
-
if (response.type ===
|
|
1036
|
+
if (response.type === import_hwk_adapter_core4.UI_RESPONSE.CANCEL) {
|
|
1025
1037
|
this.cancel();
|
|
1026
1038
|
return;
|
|
1027
1039
|
}
|
|
@@ -1034,30 +1046,30 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1034
1046
|
this.emitter.off(event, listener);
|
|
1035
1047
|
}
|
|
1036
1048
|
// ---------------------------------------------------------------------------
|
|
1037
|
-
// Explicit account import
|
|
1038
|
-
// signing calls don't each pay for their own cold-sync round trip.
|
|
1049
|
+
// Explicit account import: call before signing to avoid a cold sync per call.
|
|
1039
1050
|
// ---------------------------------------------------------------------------
|
|
1040
1051
|
async importFromQr(options = {}) {
|
|
1041
1052
|
try {
|
|
1042
|
-
return await this._jobQueue.enqueue(
|
|
1053
|
+
return await this._jobQueue.enqueue(KEYSTONE_COLD_START_JOB_LABEL, async (signal) => {
|
|
1043
1054
|
const displayDevice = placeholderDeviceInfo();
|
|
1044
1055
|
let responseUr;
|
|
1045
1056
|
if (options.mode === "scan") {
|
|
1046
|
-
responseUr = await this._requestQrScanAndAwaitResponse(displayDevice);
|
|
1057
|
+
responseUr = await this._requestQrScanAndAwaitResponse(displayDevice, signal);
|
|
1047
1058
|
} else {
|
|
1048
1059
|
const requestUr = this.urEngine.buildKeyDerivationRequest({
|
|
1049
|
-
schemas: [
|
|
1060
|
+
schemas: [keySchema("evm", KEYSTONE_WALLET_ID_PATH)],
|
|
1050
1061
|
origin: this._origin
|
|
1051
1062
|
});
|
|
1052
|
-
responseUr = await this._requestQrDisplayAndAwaitResponse(
|
|
1053
|
-
|
|
1054
|
-
animated: false
|
|
1055
|
-
|
|
1063
|
+
responseUr = await this._requestQrDisplayAndAwaitResponse(
|
|
1064
|
+
displayDevice,
|
|
1065
|
+
{ ...requestUr, animated: false },
|
|
1066
|
+
signal
|
|
1067
|
+
);
|
|
1056
1068
|
}
|
|
1057
1069
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1058
1070
|
const parsed = this.urEngine.parseAccountResponse(responseUr);
|
|
1059
1071
|
const record = this._upsertDeviceRecord(parsed);
|
|
1060
|
-
return (0,
|
|
1072
|
+
return (0, import_hwk_adapter_core4.success)(toDeviceInfo(record));
|
|
1061
1073
|
});
|
|
1062
1074
|
} catch (err) {
|
|
1063
1075
|
return this._errorToFailure(err);
|
|
@@ -1066,162 +1078,120 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1066
1078
|
// ---------------------------------------------------------------------------
|
|
1067
1079
|
// EVM
|
|
1068
1080
|
// ---------------------------------------------------------------------------
|
|
1069
|
-
async evmGetAddress(connectIdArg, deviceIdArg,
|
|
1070
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1081
|
+
async evmGetAddress(connectIdArg, deviceIdArg, params, book) {
|
|
1082
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1071
1083
|
if (!operationTarget.success) return operationTarget;
|
|
1072
1084
|
const connectId = operationTarget.payload.targetId;
|
|
1073
1085
|
const deviceId = deviceIdArg ?? void 0;
|
|
1074
|
-
|
|
1075
|
-
if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "evmGetAddress requires params");
|
|
1086
|
+
if (!params) return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "evmGetAddress requires params");
|
|
1076
1087
|
if (!params.path)
|
|
1077
|
-
return (0,
|
|
1088
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "evmGetAddress requires params.path");
|
|
1078
1089
|
const { accountPath, relativeDerivePath } = splitAccountPath(params.path);
|
|
1079
1090
|
if (!relativeDerivePath) {
|
|
1080
|
-
return (0,
|
|
1081
|
-
|
|
1091
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1092
|
+
import_hwk_adapter_core4.HardwareErrorCode.InvalidParams,
|
|
1082
1093
|
"evmGetAddress requires a full leaf path, e.g. m/44'/60'/0'/0/0"
|
|
1083
1094
|
);
|
|
1084
1095
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1098
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
|
|
1099
|
-
message: "Keystone did not return an extended public key for this account path"
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
const address = this.urEngine.deriveEvmAddressFromXpub(
|
|
1103
|
-
account.extendedPublicKey,
|
|
1104
|
-
relativeDerivePath
|
|
1105
|
-
);
|
|
1106
|
-
return (0, import_hwk_adapter_core3.success)({ address, path: normalizePath(params.path) });
|
|
1107
|
-
});
|
|
1108
|
-
} catch (err) {
|
|
1109
|
-
return this._errorToFailure(err);
|
|
1110
|
-
}
|
|
1096
|
+
return this._runJob(connectId, deviceId, async (signal) => {
|
|
1097
|
+
const xpub = await this._fetchAccountXpub(
|
|
1098
|
+
connectId,
|
|
1099
|
+
deviceId,
|
|
1100
|
+
"evm",
|
|
1101
|
+
accountPath,
|
|
1102
|
+
signal,
|
|
1103
|
+
book
|
|
1104
|
+
);
|
|
1105
|
+
const address = this.urEngine.deriveEvmAddressFromXpub(xpub, relativeDerivePath);
|
|
1106
|
+
return (0, import_hwk_adapter_core4.success)({ address, path: normalizePath(params.path) });
|
|
1107
|
+
});
|
|
1111
1108
|
}
|
|
1112
|
-
async evmSignTransaction(connectIdArg, deviceIdArg,
|
|
1113
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1109
|
+
async evmSignTransaction(connectIdArg, deviceIdArg, params) {
|
|
1110
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1114
1111
|
if (!operationTarget.success) return operationTarget;
|
|
1115
1112
|
const connectId = operationTarget.payload.targetId;
|
|
1116
1113
|
const deviceId = deviceIdArg ?? void 0;
|
|
1117
|
-
const params = paramsArg;
|
|
1118
1114
|
if (!params)
|
|
1119
|
-
return (0,
|
|
1115
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "evmSignTransaction requires params");
|
|
1120
1116
|
if (!params.path) {
|
|
1121
|
-
return (0,
|
|
1117
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "evmSignTransaction requires params.path");
|
|
1122
1118
|
}
|
|
1123
1119
|
if (!("serializedTx" in params) || typeof params.serializedTx !== "string" || !params.serializedTx) {
|
|
1124
|
-
return (0,
|
|
1125
|
-
|
|
1120
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1121
|
+
import_hwk_adapter_core4.HardwareErrorCode.MethodNotSupported,
|
|
1126
1122
|
"Keystone only signs a fully RLP-serialized transaction (params.serializedTx) \u2014 structured-field signing is not supported"
|
|
1127
1123
|
);
|
|
1128
1124
|
}
|
|
1129
|
-
const rawTxHex = (0,
|
|
1125
|
+
const rawTxHex = (0, import_hwk_adapter_core4.stripHex)(params.serializedTx);
|
|
1130
1126
|
const path = normalizePath(params.path);
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
_KeystoneAdapter._throwIfAborted(signal);
|
|
1154
|
-
const sig = this.urEngine.parseEthSignature(responseUr);
|
|
1155
|
-
_KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
|
|
1156
|
-
return (0, import_hwk_adapter_core3.success)({
|
|
1157
|
-
v: (0, import_hwk_adapter_core3.ensure0x)(sig.v),
|
|
1158
|
-
r: (0, import_hwk_adapter_core3.ensure0x)(sig.r),
|
|
1159
|
-
s: (0, import_hwk_adapter_core3.ensure0x)(sig.s)
|
|
1160
|
-
});
|
|
1161
|
-
});
|
|
1162
|
-
} catch (err) {
|
|
1163
|
-
return this._errorToFailure(err);
|
|
1164
|
-
}
|
|
1127
|
+
const dataType = parseInt(rawTxHex.slice(0, 2), 16) < 192 ? "typedTransaction" : "transaction";
|
|
1128
|
+
return this._signWithWallet({
|
|
1129
|
+
connectId,
|
|
1130
|
+
deviceId,
|
|
1131
|
+
chain: "evm",
|
|
1132
|
+
operationName: "evmSignTransaction",
|
|
1133
|
+
buildRequest: (requestId, xfp) => this.urEngine.buildEthSignRequest({
|
|
1134
|
+
requestId,
|
|
1135
|
+
unsignedTxHex: rawTxHex,
|
|
1136
|
+
dataType,
|
|
1137
|
+
path,
|
|
1138
|
+
xfp,
|
|
1139
|
+
chainId: params.chainId,
|
|
1140
|
+
origin: this._origin
|
|
1141
|
+
}),
|
|
1142
|
+
parseResponse: (ur) => this.urEngine.parseEthSignature(ur),
|
|
1143
|
+
toPayload: (sig) => ({
|
|
1144
|
+
v: (0, import_hwk_adapter_core4.ensure0x)(sig.v),
|
|
1145
|
+
r: (0, import_hwk_adapter_core4.ensure0x)(sig.r),
|
|
1146
|
+
s: (0, import_hwk_adapter_core4.ensure0x)(sig.s)
|
|
1147
|
+
})
|
|
1148
|
+
});
|
|
1165
1149
|
}
|
|
1166
|
-
async evmSignMessage(connectIdArg, deviceIdArg,
|
|
1167
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1150
|
+
async evmSignMessage(connectIdArg, deviceIdArg, params) {
|
|
1151
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1168
1152
|
if (!operationTarget.success) return operationTarget;
|
|
1169
1153
|
const connectId = operationTarget.payload.targetId;
|
|
1170
1154
|
const deviceId = deviceIdArg ?? void 0;
|
|
1171
|
-
|
|
1172
|
-
if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "evmSignMessage requires params");
|
|
1155
|
+
if (!params) return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "evmSignMessage requires params");
|
|
1173
1156
|
if (!params.path || params.message === void 0) {
|
|
1174
|
-
return (0,
|
|
1175
|
-
|
|
1157
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1158
|
+
import_hwk_adapter_core4.HardwareErrorCode.InvalidParams,
|
|
1176
1159
|
"evmSignMessage requires params.path and params.message"
|
|
1177
1160
|
);
|
|
1178
1161
|
}
|
|
1179
|
-
const messageHex = params.hex ? (0,
|
|
1162
|
+
const messageHex = params.hex ? (0, import_hwk_adapter_core4.stripHex)(params.message) : Buffer.from(params.message, "utf8").toString("hex");
|
|
1180
1163
|
const path = normalizePath(params.path);
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
connectId,
|
|
1199
|
-
signal,
|
|
1200
|
-
"evmSignMessage"
|
|
1201
|
-
);
|
|
1202
|
-
_KeystoneAdapter._throwIfAborted(signal);
|
|
1203
|
-
const sig = this.urEngine.parseEthSignature(responseUr);
|
|
1204
|
-
_KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
|
|
1205
|
-
return (0, import_hwk_adapter_core3.success)({ signature: (0, import_hwk_adapter_core3.ensure0x)(sig.r + sig.s + sig.v) });
|
|
1206
|
-
});
|
|
1207
|
-
} catch (err) {
|
|
1208
|
-
return this._errorToFailure(err);
|
|
1209
|
-
}
|
|
1164
|
+
return this._signWithWallet({
|
|
1165
|
+
connectId,
|
|
1166
|
+
deviceId,
|
|
1167
|
+
chain: "evm",
|
|
1168
|
+
operationName: "evmSignMessage",
|
|
1169
|
+
buildRequest: (requestId, xfp) => this.urEngine.buildEthSignRequest({
|
|
1170
|
+
requestId,
|
|
1171
|
+
unsignedTxHex: messageHex,
|
|
1172
|
+
dataType: "personalMessage",
|
|
1173
|
+
path,
|
|
1174
|
+
xfp,
|
|
1175
|
+
chainId: params.chainId,
|
|
1176
|
+
origin: this._origin
|
|
1177
|
+
}),
|
|
1178
|
+
parseResponse: (ur) => this.urEngine.parseEthSignature(ur),
|
|
1179
|
+
toPayload: (sig) => ({ signature: (0, import_hwk_adapter_core4.ensure0x)(sig.r + sig.s + sig.v) })
|
|
1180
|
+
});
|
|
1210
1181
|
}
|
|
1211
|
-
async evmSignTypedData(connectIdArg, deviceIdArg,
|
|
1212
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1182
|
+
async evmSignTypedData(connectIdArg, deviceIdArg, params) {
|
|
1183
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1213
1184
|
if (!operationTarget.success) return operationTarget;
|
|
1214
1185
|
const connectId = operationTarget.payload.targetId;
|
|
1215
1186
|
const deviceId = deviceIdArg ?? void 0;
|
|
1216
|
-
const params = paramsArg;
|
|
1217
1187
|
if (!params)
|
|
1218
|
-
return (0,
|
|
1188
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "evmSignTypedData requires params");
|
|
1219
1189
|
if (!params.path) {
|
|
1220
|
-
return (0,
|
|
1190
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "evmSignTypedData requires params.path");
|
|
1221
1191
|
}
|
|
1222
1192
|
if (params.mode === "hash") {
|
|
1223
|
-
return (0,
|
|
1224
|
-
|
|
1193
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1194
|
+
import_hwk_adapter_core4.HardwareErrorCode.MethodNotSupported,
|
|
1225
1195
|
"Keystone always displays the full EIP-712 payload for on-device review \u2014 pre-hashed signing is not supported"
|
|
1226
1196
|
);
|
|
1227
1197
|
}
|
|
@@ -1230,55 +1200,40 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1230
1200
|
"utf8"
|
|
1231
1201
|
).toString("hex");
|
|
1232
1202
|
const path = normalizePath(params.path);
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
connectId,
|
|
1251
|
-
signal,
|
|
1252
|
-
"evmSignTypedData"
|
|
1253
|
-
);
|
|
1254
|
-
_KeystoneAdapter._throwIfAborted(signal);
|
|
1255
|
-
const sig = this.urEngine.parseEthSignature(responseUr);
|
|
1256
|
-
_KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
|
|
1257
|
-
return (0, import_hwk_adapter_core3.success)({ signature: (0, import_hwk_adapter_core3.ensure0x)(sig.r + sig.s + sig.v) });
|
|
1258
|
-
});
|
|
1259
|
-
} catch (err) {
|
|
1260
|
-
return this._errorToFailure(err);
|
|
1261
|
-
}
|
|
1203
|
+
return this._signWithWallet({
|
|
1204
|
+
connectId,
|
|
1205
|
+
deviceId,
|
|
1206
|
+
chain: "evm",
|
|
1207
|
+
operationName: "evmSignTypedData",
|
|
1208
|
+
buildRequest: (requestId, xfp) => this.urEngine.buildEthSignRequest({
|
|
1209
|
+
requestId,
|
|
1210
|
+
unsignedTxHex: signDataHex,
|
|
1211
|
+
dataType: "typedData",
|
|
1212
|
+
path,
|
|
1213
|
+
xfp,
|
|
1214
|
+
chainId: params.chainId,
|
|
1215
|
+
origin: this._origin
|
|
1216
|
+
}),
|
|
1217
|
+
parseResponse: (ur) => this.urEngine.parseEthSignature(ur),
|
|
1218
|
+
toPayload: (sig) => ({ signature: (0, import_hwk_adapter_core4.ensure0x)(sig.r + sig.s + sig.v) })
|
|
1219
|
+
});
|
|
1262
1220
|
}
|
|
1263
1221
|
// ---------------------------------------------------------------------------
|
|
1264
|
-
// BTC
|
|
1265
|
-
//
|
|
1266
|
-
// phase doesn't wire in yet; structured-field tx signing needs host-side
|
|
1267
|
-
// PSBT construction. Both are real, bounded follow-ups, not silent gaps.
|
|
1222
|
+
// BTC: 44'/49'/84' addresses derive offline from the account xpub; P2TR addresses
|
|
1223
|
+
// and structured-field tx signing are unsupported (the host builds a PSBT instead).
|
|
1268
1224
|
// ---------------------------------------------------------------------------
|
|
1269
|
-
async btcGetAddress(connectIdArg, deviceIdArg,
|
|
1270
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1225
|
+
async btcGetAddress(connectIdArg, deviceIdArg, params, book) {
|
|
1226
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1271
1227
|
if (!operationTarget.success) return operationTarget;
|
|
1272
1228
|
const connectId = operationTarget.payload.targetId;
|
|
1273
1229
|
const deviceId = deviceIdArg ?? void 0;
|
|
1274
|
-
|
|
1275
|
-
if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "btcGetAddress requires params");
|
|
1230
|
+
if (!params) return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "btcGetAddress requires params");
|
|
1276
1231
|
if (!params.path)
|
|
1277
|
-
return (0,
|
|
1232
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "btcGetAddress requires params.path");
|
|
1278
1233
|
const scriptType = btcScriptTypeFromPath(params.path);
|
|
1279
1234
|
if (!scriptType) {
|
|
1280
|
-
return (0,
|
|
1281
|
-
|
|
1235
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1236
|
+
import_hwk_adapter_core4.HardwareErrorCode.InvalidParams,
|
|
1282
1237
|
"btcGetAddress requires a path whose purpose is 44'/49'/84'/86' (P2PKH/P2SH-P2WPKH/P2WPKH/P2TR)"
|
|
1283
1238
|
);
|
|
1284
1239
|
}
|
|
@@ -1291,100 +1246,68 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1291
1246
|
const normalizedPath = normalizePath(params.path);
|
|
1292
1247
|
const { accountPath, relativeDerivePath } = splitAccountPath(normalizedPath);
|
|
1293
1248
|
if (!relativeDerivePath) {
|
|
1294
|
-
return (0,
|
|
1295
|
-
|
|
1249
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1250
|
+
import_hwk_adapter_core4.HardwareErrorCode.InvalidParams,
|
|
1296
1251
|
"btcGetAddress requires a full leaf path, e.g. m/84'/0'/0'/0/0"
|
|
1297
1252
|
);
|
|
1298
1253
|
}
|
|
1299
1254
|
if (!isKeystoneSignableBtcAccountPath(accountPath)) {
|
|
1300
|
-
return (0,
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1315
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
|
|
1316
|
-
message: "Keystone did not return an extended public key for this account path"
|
|
1317
|
-
});
|
|
1318
|
-
}
|
|
1319
|
-
const address = this.urEngine.deriveBtcAddressFromXpub(
|
|
1320
|
-
account.extendedPublicKey,
|
|
1321
|
-
relativeDerivePath,
|
|
1322
|
-
scriptType
|
|
1323
|
-
);
|
|
1324
|
-
return (0, import_hwk_adapter_core3.success)({ address, path: normalizedPath });
|
|
1325
|
-
});
|
|
1326
|
-
} catch (err) {
|
|
1327
|
-
return this._errorToFailure(err);
|
|
1328
|
-
}
|
|
1255
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
|
|
1256
|
+
}
|
|
1257
|
+
return this._runJob(connectId, deviceId, async (signal) => {
|
|
1258
|
+
const xpub = await this._fetchAccountXpub(
|
|
1259
|
+
connectId,
|
|
1260
|
+
deviceId,
|
|
1261
|
+
"btc",
|
|
1262
|
+
accountPath,
|
|
1263
|
+
signal,
|
|
1264
|
+
book
|
|
1265
|
+
);
|
|
1266
|
+
const address = this.urEngine.deriveBtcAddressFromXpub(xpub, relativeDerivePath, scriptType);
|
|
1267
|
+
return (0, import_hwk_adapter_core4.success)({ address, path: normalizedPath });
|
|
1268
|
+
});
|
|
1329
1269
|
}
|
|
1330
1270
|
/**
|
|
1331
|
-
*
|
|
1332
|
-
*
|
|
1333
|
-
* actually syncs, and it is what a host needs to derive a whole account's
|
|
1334
|
-
* addresses offline. Unlike `btcGetAddress` this has no script-type
|
|
1335
|
-
* restriction: an xpub is script-type agnostic, so `86'` (taproot) works
|
|
1336
|
-
* here even though deriving a taproot ADDRESS still needs an EC library
|
|
1337
|
-
* this package doesn't wire in.
|
|
1271
|
+
* `params.path` must be an account path (`m/84'/0'/0'`), the level Keystone syncs. An xpub is
|
|
1272
|
+
* script-type agnostic, so `86'` works here even though `btcGetAddress` rejects it.
|
|
1338
1273
|
*/
|
|
1339
|
-
async btcGetPublicKey(connectIdArg, deviceIdArg,
|
|
1340
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1274
|
+
async btcGetPublicKey(connectIdArg, deviceIdArg, params, book) {
|
|
1275
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1341
1276
|
if (!operationTarget.success) return operationTarget;
|
|
1342
1277
|
const connectId = operationTarget.payload.targetId;
|
|
1343
1278
|
const deviceId = deviceIdArg ?? void 0;
|
|
1344
|
-
|
|
1345
|
-
if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "btcGetPublicKey requires params");
|
|
1279
|
+
if (!params) return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "btcGetPublicKey requires params");
|
|
1346
1280
|
if (!params.path)
|
|
1347
|
-
return (0,
|
|
1281
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "btcGetPublicKey requires params.path");
|
|
1348
1282
|
const { accountPath, relativeDerivePath } = splitAccountPath(params.path);
|
|
1349
1283
|
if (relativeDerivePath) {
|
|
1350
|
-
return (0,
|
|
1351
|
-
|
|
1284
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1285
|
+
import_hwk_adapter_core4.HardwareErrorCode.InvalidParams,
|
|
1352
1286
|
"btcGetPublicKey requires an account path, e.g. m/84'/0'/0' (not a leaf path)"
|
|
1353
1287
|
);
|
|
1354
1288
|
}
|
|
1355
1289
|
if (!isKeystoneSignableBtcAccountPath(accountPath)) {
|
|
1356
|
-
return (0,
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
const meta = this.urEngine.parseXpubMeta(account.extendedPublicKey);
|
|
1376
|
-
return (0, import_hwk_adapter_core3.success)({
|
|
1377
|
-
xpub: account.extendedPublicKey,
|
|
1378
|
-
publicKey: meta.publicKey,
|
|
1379
|
-
chainCode: meta.chainCode,
|
|
1380
|
-
depth: meta.depth,
|
|
1381
|
-
fingerprint: meta.parentFingerprint,
|
|
1382
|
-
path: normalizePath(accountPath)
|
|
1383
|
-
});
|
|
1290
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
|
|
1291
|
+
}
|
|
1292
|
+
return this._runJob(connectId, deviceId, async (signal) => {
|
|
1293
|
+
const xpub = await this._fetchAccountXpub(
|
|
1294
|
+
connectId,
|
|
1295
|
+
deviceId,
|
|
1296
|
+
"btc",
|
|
1297
|
+
accountPath,
|
|
1298
|
+
signal,
|
|
1299
|
+
book
|
|
1300
|
+
);
|
|
1301
|
+
const meta = this.urEngine.parseXpubMeta(xpub);
|
|
1302
|
+
return (0, import_hwk_adapter_core4.success)({
|
|
1303
|
+
xpub,
|
|
1304
|
+
publicKey: meta.publicKey,
|
|
1305
|
+
chainCode: meta.chainCode,
|
|
1306
|
+
depth: meta.depth,
|
|
1307
|
+
fingerprint: meta.parentFingerprint,
|
|
1308
|
+
path: normalizePath(accountPath)
|
|
1384
1309
|
});
|
|
1385
|
-
}
|
|
1386
|
-
return this._errorToFailure(err);
|
|
1387
|
-
}
|
|
1310
|
+
});
|
|
1388
1311
|
}
|
|
1389
1312
|
async btcSignTransaction(_connectId, _deviceId, _params) {
|
|
1390
1313
|
return this._unsupported(
|
|
@@ -1392,372 +1315,319 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1392
1315
|
"use btcSignPsbt \u2014 structured input/output signing needs host-side PSBT construction, not yet wired in"
|
|
1393
1316
|
);
|
|
1394
1317
|
}
|
|
1395
|
-
async btcSignPsbt(connectIdArg, deviceIdArg,
|
|
1396
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1318
|
+
async btcSignPsbt(connectIdArg, deviceIdArg, params) {
|
|
1319
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1397
1320
|
if (!operationTarget.success) return operationTarget;
|
|
1398
1321
|
const connectId = operationTarget.payload.targetId;
|
|
1399
1322
|
const deviceId = deviceIdArg ?? void 0;
|
|
1400
|
-
|
|
1401
|
-
if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "btcSignPsbt requires params");
|
|
1323
|
+
if (!params) return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "btcSignPsbt requires params");
|
|
1402
1324
|
if (!params.psbt)
|
|
1403
|
-
return (0,
|
|
1325
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "btcSignPsbt requires params.psbt");
|
|
1404
1326
|
if (!params.path)
|
|
1405
|
-
return (0,
|
|
1327
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "btcSignPsbt requires params.path");
|
|
1406
1328
|
if (!isKeystoneSignableBtcAccountPath(splitAccountPath(params.path).accountPath)) {
|
|
1407
|
-
return (0,
|
|
1408
|
-
}
|
|
1409
|
-
try {
|
|
1410
|
-
return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
|
|
1411
|
-
const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
|
|
1412
|
-
_KeystoneAdapter._throwIfAborted(signal);
|
|
1413
|
-
const requestUr = this.urEngine.buildBtcPsbtRequest((0, import_hwk_adapter_core3.stripHex)(params.psbt));
|
|
1414
|
-
const responseUr = await this._resolveUr(
|
|
1415
|
-
record,
|
|
1416
|
-
requestUr,
|
|
1417
|
-
true,
|
|
1418
|
-
connectId,
|
|
1419
|
-
signal,
|
|
1420
|
-
"btcSignPsbt"
|
|
1421
|
-
);
|
|
1422
|
-
_KeystoneAdapter._throwIfAborted(signal);
|
|
1423
|
-
const signedPsbt = this.urEngine.parseBtcPsbt(responseUr);
|
|
1424
|
-
return (0, import_hwk_adapter_core3.success)({ signedPsbt });
|
|
1425
|
-
});
|
|
1426
|
-
} catch (err) {
|
|
1427
|
-
return this._errorToFailure(err);
|
|
1329
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
|
|
1428
1330
|
}
|
|
1331
|
+
return this._runJob(connectId, deviceId, async (signal) => {
|
|
1332
|
+
const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
|
|
1333
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
1334
|
+
const requestUr = this.urEngine.buildBtcPsbtRequest((0, import_hwk_adapter_core4.stripHex)(params.psbt));
|
|
1335
|
+
const responseUr = await this._resolveUr(
|
|
1336
|
+
record,
|
|
1337
|
+
requestUr,
|
|
1338
|
+
true,
|
|
1339
|
+
connectId,
|
|
1340
|
+
signal,
|
|
1341
|
+
"btcSignPsbt"
|
|
1342
|
+
);
|
|
1343
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
1344
|
+
const signedPsbt = this.urEngine.parseBtcPsbt(responseUr);
|
|
1345
|
+
return (0, import_hwk_adapter_core4.success)({ signedPsbt });
|
|
1346
|
+
});
|
|
1429
1347
|
}
|
|
1430
|
-
async btcSignMessage(connectIdArg, deviceIdArg,
|
|
1431
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1348
|
+
async btcSignMessage(connectIdArg, deviceIdArg, params) {
|
|
1349
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1432
1350
|
if (!operationTarget.success) return operationTarget;
|
|
1433
1351
|
const connectId = operationTarget.payload.targetId;
|
|
1434
1352
|
const deviceId = deviceIdArg ?? void 0;
|
|
1435
|
-
|
|
1436
|
-
if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "btcSignMessage requires params");
|
|
1353
|
+
if (!params) return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "btcSignMessage requires params");
|
|
1437
1354
|
if (!params.path || params.message === void 0) {
|
|
1438
|
-
return (0,
|
|
1439
|
-
|
|
1355
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1356
|
+
import_hwk_adapter_core4.HardwareErrorCode.InvalidParams,
|
|
1440
1357
|
"btcSignMessage requires params.path and params.message"
|
|
1441
1358
|
);
|
|
1442
1359
|
}
|
|
1443
|
-
const messageHex = params.hex ? (0,
|
|
1360
|
+
const messageHex = params.hex ? (0, import_hwk_adapter_core4.stripHex)(params.message) : Buffer.from(params.message, "utf8").toString("hex");
|
|
1444
1361
|
const path = normalizePath(params.path);
|
|
1445
1362
|
if (!isKeystoneSignableBtcAccountPath(splitAccountPath(path).accountPath)) {
|
|
1446
|
-
return (0,
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
connectId,
|
|
1463
|
-
signal,
|
|
1464
|
-
"btcSignMessage"
|
|
1465
|
-
);
|
|
1466
|
-
_KeystoneAdapter._throwIfAborted(signal);
|
|
1467
|
-
const sig = this.urEngine.parseBtcSignature(responseUr);
|
|
1468
|
-
_KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
|
|
1469
|
-
return (0, import_hwk_adapter_core3.success)({ signature: sig.signature });
|
|
1470
|
-
});
|
|
1471
|
-
} catch (err) {
|
|
1472
|
-
return this._errorToFailure(err);
|
|
1473
|
-
}
|
|
1363
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
|
|
1364
|
+
}
|
|
1365
|
+
return this._signWithWallet({
|
|
1366
|
+
connectId,
|
|
1367
|
+
deviceId,
|
|
1368
|
+
chain: "btc",
|
|
1369
|
+
operationName: "btcSignMessage",
|
|
1370
|
+
buildRequest: (requestId, xfp) => this.urEngine.buildBtcMessageSignRequest({
|
|
1371
|
+
requestId,
|
|
1372
|
+
messageHex,
|
|
1373
|
+
accounts: [{ path, xfp }],
|
|
1374
|
+
origin: this._origin
|
|
1375
|
+
}),
|
|
1376
|
+
parseResponse: (ur) => this.urEngine.parseBtcSignature(ur),
|
|
1377
|
+
toPayload: (sig) => ({ signature: sig.signature })
|
|
1378
|
+
});
|
|
1474
1379
|
}
|
|
1475
1380
|
async btcGetMasterFingerprint(connectIdArg, deviceIdArg, paramsArg) {
|
|
1476
1381
|
const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
|
|
1477
1382
|
if (!operationTarget.success) return operationTarget;
|
|
1478
1383
|
const connectId = operationTarget.payload.targetId;
|
|
1479
1384
|
const deviceId = deviceIdArg ?? void 0;
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
});
|
|
1485
|
-
} catch (err) {
|
|
1486
|
-
return this._errorToFailure(err);
|
|
1487
|
-
}
|
|
1385
|
+
return this._runJob(connectId, deviceId, async (signal) => {
|
|
1386
|
+
const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
|
|
1387
|
+
return (0, import_hwk_adapter_core4.success)({ masterFingerprint: record.masterFingerprint });
|
|
1388
|
+
});
|
|
1488
1389
|
}
|
|
1489
1390
|
// ---------------------------------------------------------------------------
|
|
1490
1391
|
// SOL
|
|
1491
1392
|
// ---------------------------------------------------------------------------
|
|
1492
|
-
async solGetAddress(connectIdArg, deviceIdArg,
|
|
1493
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1393
|
+
async solGetAddress(connectIdArg, deviceIdArg, params, book) {
|
|
1394
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1494
1395
|
if (!operationTarget.success) return operationTarget;
|
|
1495
1396
|
const connectId = operationTarget.payload.targetId;
|
|
1496
1397
|
const deviceId = deviceIdArg ?? void 0;
|
|
1497
|
-
|
|
1498
|
-
if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "solGetAddress requires params");
|
|
1398
|
+
if (!params) return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "solGetAddress requires params");
|
|
1499
1399
|
if (!params.path)
|
|
1500
|
-
return (0,
|
|
1400
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "solGetAddress requires params.path");
|
|
1501
1401
|
const path = normalizePath(params.path);
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
const address = import_bs58.default.encode(Buffer.from(account.publicKey, "hex"));
|
|
1509
|
-
return (0, import_hwk_adapter_core3.success)({ address, path });
|
|
1510
|
-
});
|
|
1511
|
-
} catch (err) {
|
|
1512
|
-
return this._errorToFailure(err);
|
|
1513
|
-
}
|
|
1402
|
+
return this._runJob(connectId, deviceId, async (signal) => {
|
|
1403
|
+
const { account } = await this._fetchAccount(connectId, deviceId, "sol", path, signal, book);
|
|
1404
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
1405
|
+
const address = import_bs58.default.encode(Buffer.from(account.publicKey, "hex"));
|
|
1406
|
+
return (0, import_hwk_adapter_core4.success)({ address, path });
|
|
1407
|
+
});
|
|
1514
1408
|
}
|
|
1515
|
-
async solSignTransaction(connectIdArg, deviceIdArg,
|
|
1516
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1409
|
+
async solSignTransaction(connectIdArg, deviceIdArg, params) {
|
|
1410
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1517
1411
|
if (!operationTarget.success) return operationTarget;
|
|
1518
1412
|
const connectId = operationTarget.payload.targetId;
|
|
1519
1413
|
const deviceId = deviceIdArg ?? void 0;
|
|
1520
|
-
const params = paramsArg;
|
|
1521
1414
|
if (!params)
|
|
1522
|
-
return (0,
|
|
1415
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "solSignTransaction requires params");
|
|
1523
1416
|
if (!params.path || !params.serializedTx) {
|
|
1524
|
-
return (0,
|
|
1525
|
-
|
|
1417
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1418
|
+
import_hwk_adapter_core4.HardwareErrorCode.InvalidParams,
|
|
1526
1419
|
"solSignTransaction requires params.path and params.serializedTx"
|
|
1527
1420
|
);
|
|
1528
1421
|
}
|
|
1529
1422
|
const path = normalizePath(params.path);
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
connectId,
|
|
1547
|
-
signal,
|
|
1548
|
-
"solSignTransaction"
|
|
1549
|
-
);
|
|
1550
|
-
_KeystoneAdapter._throwIfAborted(signal);
|
|
1551
|
-
const sig = this.urEngine.parseSolSignature(responseUr);
|
|
1552
|
-
_KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
|
|
1553
|
-
return (0, import_hwk_adapter_core3.success)({ signature: sig.signature });
|
|
1554
|
-
});
|
|
1555
|
-
} catch (err) {
|
|
1556
|
-
return this._errorToFailure(err);
|
|
1557
|
-
}
|
|
1423
|
+
return this._signWithWallet({
|
|
1424
|
+
connectId,
|
|
1425
|
+
deviceId,
|
|
1426
|
+
chain: "sol",
|
|
1427
|
+
operationName: "solSignTransaction",
|
|
1428
|
+
buildRequest: (requestId, xfp) => this.urEngine.buildSolSignRequest({
|
|
1429
|
+
requestId,
|
|
1430
|
+
unsignedPayloadHex: (0, import_hwk_adapter_core4.stripHex)(params.serializedTx),
|
|
1431
|
+
dataType: "transaction",
|
|
1432
|
+
path,
|
|
1433
|
+
xfp,
|
|
1434
|
+
origin: this._origin
|
|
1435
|
+
}),
|
|
1436
|
+
parseResponse: (ur) => this.urEngine.parseSolSignature(ur),
|
|
1437
|
+
toPayload: (sig) => ({ signature: sig.signature })
|
|
1438
|
+
});
|
|
1558
1439
|
}
|
|
1559
|
-
async solSignMessage(connectIdArg, deviceIdArg,
|
|
1560
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1440
|
+
async solSignMessage(connectIdArg, deviceIdArg, params) {
|
|
1441
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1561
1442
|
if (!operationTarget.success) return operationTarget;
|
|
1562
1443
|
const connectId = operationTarget.payload.targetId;
|
|
1563
1444
|
const deviceId = deviceIdArg ?? void 0;
|
|
1564
|
-
|
|
1565
|
-
if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "solSignMessage requires params");
|
|
1445
|
+
if (!params) return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "solSignMessage requires params");
|
|
1566
1446
|
if (!params.path || !params.message) {
|
|
1567
|
-
return (0,
|
|
1568
|
-
|
|
1447
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1448
|
+
import_hwk_adapter_core4.HardwareErrorCode.InvalidParams,
|
|
1569
1449
|
"solSignMessage requires params.path and params.message"
|
|
1570
1450
|
);
|
|
1571
1451
|
}
|
|
1572
1452
|
const path = normalizePath(params.path);
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
connectId,
|
|
1590
|
-
signal,
|
|
1591
|
-
"solSignMessage"
|
|
1592
|
-
);
|
|
1593
|
-
_KeystoneAdapter._throwIfAborted(signal);
|
|
1594
|
-
const sig = this.urEngine.parseSolSignature(responseUr);
|
|
1595
|
-
_KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
|
|
1596
|
-
return (0, import_hwk_adapter_core3.success)({ signature: sig.signature });
|
|
1597
|
-
});
|
|
1598
|
-
} catch (err) {
|
|
1599
|
-
return this._errorToFailure(err);
|
|
1600
|
-
}
|
|
1453
|
+
return this._signWithWallet({
|
|
1454
|
+
connectId,
|
|
1455
|
+
deviceId,
|
|
1456
|
+
chain: "sol",
|
|
1457
|
+
operationName: "solSignMessage",
|
|
1458
|
+
buildRequest: (requestId, xfp) => this.urEngine.buildSolSignRequest({
|
|
1459
|
+
requestId,
|
|
1460
|
+
unsignedPayloadHex: (0, import_hwk_adapter_core4.stripHex)(params.message),
|
|
1461
|
+
dataType: "message",
|
|
1462
|
+
path,
|
|
1463
|
+
xfp,
|
|
1464
|
+
origin: this._origin
|
|
1465
|
+
}),
|
|
1466
|
+
parseResponse: (ur) => this.urEngine.parseSolSignature(ur),
|
|
1467
|
+
toPayload: (sig) => ({ signature: sig.signature })
|
|
1468
|
+
});
|
|
1601
1469
|
}
|
|
1602
1470
|
// ---------------------------------------------------------------------------
|
|
1603
|
-
// TRON
|
|
1604
|
-
//
|
|
1605
|
-
// (tags 5201/5202, firmware >= 2.5.0). The OneKey Pro air-gap encoding under
|
|
1606
|
-
// the same tag is a different layout the firmware rejects. Not keystone-sdk's
|
|
1607
|
-
// bundled `sdk.tron` module either (protobuf-based, unverified response
|
|
1608
|
-
// semantics).
|
|
1471
|
+
// TRON: keystone-sdk-rust layout (tags 5201/5202, firmware >= 2.5.0), not sdk.tron's
|
|
1472
|
+
// protobuf module; OneKey Pro's encoding under the same tag is rejected by the firmware.
|
|
1609
1473
|
// ---------------------------------------------------------------------------
|
|
1610
|
-
async tronGetAddress(connectIdArg, deviceIdArg,
|
|
1611
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1474
|
+
async tronGetAddress(connectIdArg, deviceIdArg, params, book) {
|
|
1475
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1612
1476
|
if (!operationTarget.success) return operationTarget;
|
|
1613
1477
|
const connectId = operationTarget.payload.targetId;
|
|
1614
1478
|
const deviceId = deviceIdArg ?? void 0;
|
|
1615
|
-
|
|
1616
|
-
if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "tronGetAddress requires params");
|
|
1479
|
+
if (!params) return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "tronGetAddress requires params");
|
|
1617
1480
|
if (!params.path)
|
|
1618
|
-
return (0,
|
|
1481
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "tronGetAddress requires params.path");
|
|
1619
1482
|
const { accountPath, relativeDerivePath } = splitAccountPath(params.path);
|
|
1620
1483
|
if (!relativeDerivePath) {
|
|
1621
|
-
return (0,
|
|
1622
|
-
|
|
1484
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1485
|
+
import_hwk_adapter_core4.HardwareErrorCode.InvalidParams,
|
|
1623
1486
|
"tronGetAddress requires a full leaf path, e.g. m/44'/195'/0'/0/0"
|
|
1624
1487
|
);
|
|
1625
1488
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1639
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
|
|
1640
|
-
message: "Keystone did not return an extended public key for this account path"
|
|
1641
|
-
});
|
|
1642
|
-
}
|
|
1643
|
-
const address = this.urEngine.deriveTronAddressFromXpub(
|
|
1644
|
-
account.extendedPublicKey,
|
|
1645
|
-
relativeDerivePath
|
|
1646
|
-
);
|
|
1647
|
-
return (0, import_hwk_adapter_core3.success)({ address, path: normalizePath(params.path) });
|
|
1648
|
-
});
|
|
1649
|
-
} catch (err) {
|
|
1650
|
-
return this._errorToFailure(err);
|
|
1651
|
-
}
|
|
1489
|
+
return this._runJob(connectId, deviceId, async (signal) => {
|
|
1490
|
+
const xpub = await this._fetchAccountXpub(
|
|
1491
|
+
connectId,
|
|
1492
|
+
deviceId,
|
|
1493
|
+
"tron",
|
|
1494
|
+
accountPath,
|
|
1495
|
+
signal,
|
|
1496
|
+
book
|
|
1497
|
+
);
|
|
1498
|
+
const address = this.urEngine.deriveTronAddressFromXpub(xpub, relativeDerivePath);
|
|
1499
|
+
return (0, import_hwk_adapter_core4.success)({ address, path: normalizePath(params.path) });
|
|
1500
|
+
});
|
|
1652
1501
|
}
|
|
1653
|
-
async tronSignTransaction(connectIdArg, deviceIdArg,
|
|
1654
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1502
|
+
async tronSignTransaction(connectIdArg, deviceIdArg, params) {
|
|
1503
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1655
1504
|
if (!operationTarget.success) return operationTarget;
|
|
1656
1505
|
const connectId = operationTarget.payload.targetId;
|
|
1657
1506
|
const deviceId = deviceIdArg ?? void 0;
|
|
1658
|
-
const params = paramsArg;
|
|
1659
1507
|
if (!params)
|
|
1660
|
-
return (0,
|
|
1508
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "tronSignTransaction requires params");
|
|
1661
1509
|
if (!params.path) {
|
|
1662
|
-
return (0,
|
|
1510
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "tronSignTransaction requires params.path");
|
|
1663
1511
|
}
|
|
1664
1512
|
if (!params.rawTxHex) {
|
|
1665
|
-
return (0,
|
|
1666
|
-
|
|
1513
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1514
|
+
import_hwk_adapter_core4.HardwareErrorCode.InvalidParams,
|
|
1667
1515
|
"Keystone only signs a fully protobuf-serialized TRON transaction (params.rawTxHex) \u2014 the Trezor-style structured contract fields have no equivalent here"
|
|
1668
1516
|
);
|
|
1669
1517
|
}
|
|
1670
|
-
const rawTxHex = (0,
|
|
1518
|
+
const rawTxHex = (0, import_hwk_adapter_core4.stripHex)(params.rawTxHex);
|
|
1671
1519
|
const path = normalizePath(params.path);
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
signal,
|
|
1689
|
-
"tronSignTransaction"
|
|
1690
|
-
);
|
|
1691
|
-
_KeystoneAdapter._throwIfAborted(signal);
|
|
1692
|
-
const sig = this.urEngine.parseTronSignature(responseUr);
|
|
1693
|
-
_KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
|
|
1694
|
-
return (0, import_hwk_adapter_core3.success)({ signature: sig.signature });
|
|
1695
|
-
});
|
|
1696
|
-
} catch (err) {
|
|
1697
|
-
return this._errorToFailure(err);
|
|
1698
|
-
}
|
|
1520
|
+
return this._signWithWallet({
|
|
1521
|
+
connectId,
|
|
1522
|
+
deviceId,
|
|
1523
|
+
chain: "tron",
|
|
1524
|
+
operationName: "tronSignTransaction",
|
|
1525
|
+
buildRequest: (requestId, xfp) => this.urEngine.buildTronSignRequest({
|
|
1526
|
+
requestId,
|
|
1527
|
+
rawTxHex,
|
|
1528
|
+
path,
|
|
1529
|
+
xfp,
|
|
1530
|
+
origin: this._origin
|
|
1531
|
+
}),
|
|
1532
|
+
parseResponse: (ur) => this.urEngine.parseTronSignature(ur),
|
|
1533
|
+
// Keystone signs rawTxHex as given and returns only the signature, so no serializedTx.
|
|
1534
|
+
toPayload: (sig) => ({ signature: sig.signature })
|
|
1535
|
+
});
|
|
1699
1536
|
}
|
|
1700
|
-
async tronSignMessage(connectIdArg, deviceIdArg,
|
|
1701
|
-
const operationTarget = resolveHardwareOperationTarget(connectIdArg,
|
|
1537
|
+
async tronSignMessage(connectIdArg, deviceIdArg, params) {
|
|
1538
|
+
const operationTarget = resolveHardwareOperationTarget(connectIdArg, params?.operationId);
|
|
1702
1539
|
if (!operationTarget.success) return operationTarget;
|
|
1703
1540
|
const connectId = operationTarget.payload.targetId;
|
|
1704
1541
|
const deviceId = deviceIdArg ?? void 0;
|
|
1705
|
-
|
|
1706
|
-
if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "tronSignMessage requires params");
|
|
1542
|
+
if (!params) return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "tronSignMessage requires params");
|
|
1707
1543
|
if (!params.path) {
|
|
1708
|
-
return (0,
|
|
1544
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "tronSignMessage requires params.path");
|
|
1709
1545
|
}
|
|
1710
1546
|
if (!params.messageHex) {
|
|
1711
|
-
return (0,
|
|
1547
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.InvalidParams, "tronSignMessage requires params.messageHex");
|
|
1712
1548
|
}
|
|
1713
1549
|
if (params.messageType !== "V2") {
|
|
1714
|
-
return (0,
|
|
1715
|
-
|
|
1550
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1551
|
+
import_hwk_adapter_core4.HardwareErrorCode.MethodNotSupported,
|
|
1716
1552
|
'Keystone signs TRON messages as TIP-191 signMessageV2 only (messageType: "V2")'
|
|
1717
1553
|
);
|
|
1718
1554
|
}
|
|
1719
|
-
const messageHex = (0,
|
|
1555
|
+
const messageHex = (0, import_hwk_adapter_core4.stripHex)(params.messageHex);
|
|
1720
1556
|
const path = normalizePath(params.path);
|
|
1557
|
+
return this._signWithWallet({
|
|
1558
|
+
connectId,
|
|
1559
|
+
deviceId,
|
|
1560
|
+
chain: "tron",
|
|
1561
|
+
operationName: "tronSignMessage",
|
|
1562
|
+
buildRequest: (requestId, xfp) => this.urEngine.buildTronSignRequest({
|
|
1563
|
+
requestId,
|
|
1564
|
+
rawTxHex: messageHex,
|
|
1565
|
+
path,
|
|
1566
|
+
xfp,
|
|
1567
|
+
signType: 2 /* PersonalMessage */,
|
|
1568
|
+
origin: this._origin
|
|
1569
|
+
}),
|
|
1570
|
+
parseResponse: (ur) => this.urEngine.parseTronSignature(ur),
|
|
1571
|
+
toPayload: (sig) => ({ signature: sig.signature })
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
// ---------------------------------------------------------------------------
|
|
1575
|
+
// Internals
|
|
1576
|
+
// ---------------------------------------------------------------------------
|
|
1577
|
+
async _runJob(connectId, deviceId, job) {
|
|
1721
1578
|
try {
|
|
1722
|
-
return await this._jobQueue.enqueue(
|
|
1723
|
-
const { record } = await this._ensureWalletKnown(connectId, deviceId, "tron", signal);
|
|
1724
|
-
_KeystoneAdapter._throwIfAborted(signal);
|
|
1725
|
-
const requestId = (0, import_uuid2.v4)();
|
|
1726
|
-
const requestUr = this.urEngine.buildTronSignRequest({
|
|
1727
|
-
requestId,
|
|
1728
|
-
rawTxHex: messageHex,
|
|
1729
|
-
path,
|
|
1730
|
-
xfp: record.masterFingerprint,
|
|
1731
|
-
signType: 2 /* PersonalMessage */
|
|
1732
|
-
});
|
|
1733
|
-
const responseUr = await this._resolveUr(
|
|
1734
|
-
record,
|
|
1735
|
-
requestUr,
|
|
1736
|
-
true,
|
|
1737
|
-
connectId,
|
|
1738
|
-
signal,
|
|
1739
|
-
"tronSignMessage"
|
|
1740
|
-
);
|
|
1741
|
-
_KeystoneAdapter._throwIfAborted(signal);
|
|
1742
|
-
const sig = this.urEngine.parseTronSignature(responseUr);
|
|
1743
|
-
_KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
|
|
1744
|
-
return (0, import_hwk_adapter_core3.success)({ signature: sig.signature });
|
|
1745
|
-
});
|
|
1579
|
+
return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), job);
|
|
1746
1580
|
} catch (err) {
|
|
1747
1581
|
return this._errorToFailure(err);
|
|
1748
1582
|
}
|
|
1749
1583
|
}
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1584
|
+
async _fetchAccountXpub(connectId, deviceId, hwkChain, accountPath, signal, book) {
|
|
1585
|
+
const { account } = await this._fetchAccount(
|
|
1586
|
+
connectId,
|
|
1587
|
+
deviceId,
|
|
1588
|
+
hwkChain,
|
|
1589
|
+
accountPath,
|
|
1590
|
+
signal,
|
|
1591
|
+
book
|
|
1592
|
+
);
|
|
1593
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
1594
|
+
if (!account.extendedPublicKey) {
|
|
1595
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1596
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.MethodNotSupported,
|
|
1597
|
+
message: "Keystone did not return an extended public key for this account path"
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
return account.extendedPublicKey;
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Signing needs only the wallet's mfp: the device re-derives the key from path+xfp, so no
|
|
1604
|
+
* per-path account lookup happens here.
|
|
1605
|
+
*/
|
|
1606
|
+
_signWithWallet(args) {
|
|
1607
|
+
const { connectId, deviceId } = args;
|
|
1608
|
+
return this._runJob(connectId, deviceId, async (signal) => {
|
|
1609
|
+
const { record } = await this._ensureWalletKnown(connectId, deviceId, args.chain, signal);
|
|
1610
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
1611
|
+
const requestId = (0, import_uuid2.v4)();
|
|
1612
|
+
const requestUr = args.buildRequest(requestId, record.masterFingerprint);
|
|
1613
|
+
const responseUr = await this._resolveUr(
|
|
1614
|
+
record,
|
|
1615
|
+
requestUr,
|
|
1616
|
+
true,
|
|
1617
|
+
connectId,
|
|
1618
|
+
signal,
|
|
1619
|
+
args.operationName
|
|
1620
|
+
);
|
|
1621
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
1622
|
+
const signature = args.parseResponse(responseUr);
|
|
1623
|
+
_KeystoneAdapter._assertRequestIdMatches(requestId, signature.requestId);
|
|
1624
|
+
return (0, import_hwk_adapter_core4.success)(args.toPayload(signature));
|
|
1625
|
+
});
|
|
1756
1626
|
}
|
|
1757
1627
|
_allNetworkSyncSchema(item) {
|
|
1758
|
-
if (!(0,
|
|
1628
|
+
if (!(0, import_hwk_adapter_core4.isAllNetworkMethodName)(String(item.methodName)) || !item.path) return void 0;
|
|
1759
1629
|
const method = item.methodName;
|
|
1760
|
-
const hwkChain = (0,
|
|
1630
|
+
const hwkChain = (0, import_hwk_adapter_core4.getAllNetworkMethodChain)(method);
|
|
1761
1631
|
const path = normalizePath(item.path);
|
|
1762
1632
|
if (hwkChain === "btc" && !isKeystoneSignableBtcAccountPath(splitAccountPath(path).accountPath)) {
|
|
1763
1633
|
return void 0;
|
|
@@ -1775,16 +1645,13 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1775
1645
|
}
|
|
1776
1646
|
}
|
|
1777
1647
|
/**
|
|
1778
|
-
* QR
|
|
1779
|
-
*
|
|
1780
|
-
* chains or derivation paths the host bundled. USB keeps its proven
|
|
1781
|
-
* one-path-at-a-time export flow because the device limits that channel.
|
|
1648
|
+
* QR answers the whole bundle with one request QR and one scan. USB exports one path per
|
|
1649
|
+
* request because the device limits that channel.
|
|
1782
1650
|
*/
|
|
1783
1651
|
async _prefetchAllNetworkAccounts(connectId, deviceId, params) {
|
|
1784
|
-
return this._jobQueue.enqueue(
|
|
1652
|
+
return this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
|
|
1785
1653
|
const target = this._resolveTarget(connectId, deviceId);
|
|
1786
|
-
|
|
1787
|
-
let record = targetRecord;
|
|
1654
|
+
let { record } = target;
|
|
1788
1655
|
const requestedByKey = /* @__PURE__ */ new Map();
|
|
1789
1656
|
for (const item of params.bundle) {
|
|
1790
1657
|
const schema = this._allNetworkSyncSchema(item);
|
|
@@ -1794,11 +1661,23 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1794
1661
|
if (!requestedByKey.size) {
|
|
1795
1662
|
return { book };
|
|
1796
1663
|
}
|
|
1797
|
-
const
|
|
1664
|
+
const requestedSchemas = Array.from(requestedByKey.values());
|
|
1665
|
+
const assertAllBooked = () => {
|
|
1666
|
+
for (const schema of requestedSchemas) {
|
|
1667
|
+
if (!book.has(accountKey(schema.hwkChain, schema.path))) {
|
|
1668
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1669
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch,
|
|
1670
|
+
message: `Keystone did not return the requested derivation path (${schema.path})`
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
const operation = (0, import_hwk_adapter_core4.isHardwareOperationId)(params.operationId) ? this._operations.find(params.operationId) : void 0;
|
|
1798
1676
|
const operationRoute = operation ? this._operationRoutes.get(operation.connectId) : void 0;
|
|
1799
1677
|
const routedToQr = operationRoute?.operationId === operation?.operationId && operationRoute?.connectionType === "qr";
|
|
1800
1678
|
if (this._forcedTransport !== "qr" && !routedToQr && !record?.usbSessionId && this._usbConnector && target.expectedWalletId) {
|
|
1801
1679
|
record = await this._tryUsbAttach(
|
|
1680
|
+
signal,
|
|
1802
1681
|
target.expectedWalletId,
|
|
1803
1682
|
record?.masterFingerprint ?? target.expectedMasterFingerprint
|
|
1804
1683
|
) ?? record;
|
|
@@ -1808,14 +1687,13 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1808
1687
|
const bundleSessionId = record.usbSessionId;
|
|
1809
1688
|
const assertBundleSession = () => {
|
|
1810
1689
|
if (record?.usbSessionId !== bundleSessionId) {
|
|
1811
|
-
throw (0,
|
|
1812
|
-
code:
|
|
1690
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1691
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.TransportNotAvailable,
|
|
1813
1692
|
message: "Keystone USB connection was lost during account export"
|
|
1814
1693
|
});
|
|
1815
1694
|
}
|
|
1816
1695
|
};
|
|
1817
|
-
const
|
|
1818
|
-
for (const schema of missingSchemas2) {
|
|
1696
|
+
for (const schema of requestedSchemas) {
|
|
1819
1697
|
if (!book.has(accountKey(schema.hwkChain, schema.path))) {
|
|
1820
1698
|
assertBundleSession();
|
|
1821
1699
|
const synced = await this._fetchAccount(
|
|
@@ -1824,43 +1702,35 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1824
1702
|
schema.hwkChain,
|
|
1825
1703
|
schema.path,
|
|
1826
1704
|
signal,
|
|
1827
|
-
|
|
1705
|
+
book
|
|
1828
1706
|
);
|
|
1829
1707
|
record = synced.record;
|
|
1830
1708
|
assertBundleSession();
|
|
1831
1709
|
}
|
|
1832
1710
|
}
|
|
1833
|
-
|
|
1834
|
-
if (!book.has(accountKey(schema.hwkChain, schema.path))) {
|
|
1835
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1836
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
|
|
1837
|
-
message: `Keystone did not return the requested derivation path (${schema.path})`
|
|
1838
|
-
});
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1711
|
+
assertAllBooked();
|
|
1841
1712
|
return { book, walletId: record.walletId };
|
|
1842
1713
|
}
|
|
1843
1714
|
if (this._forcedTransport === "usb") {
|
|
1844
|
-
throw (0,
|
|
1845
|
-
code:
|
|
1715
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1716
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.TransportNotAvailable,
|
|
1846
1717
|
message: "USB channel is not connected for this Keystone wallet"
|
|
1847
1718
|
});
|
|
1848
1719
|
}
|
|
1849
|
-
const missingSchemas = Array.from(requestedByKey.values());
|
|
1850
1720
|
const schemas = [
|
|
1851
1721
|
{ hwkChain: "evm", path: KEYSTONE_WALLET_ID_PATH },
|
|
1852
|
-
...
|
|
1722
|
+
...requestedSchemas.filter(
|
|
1723
|
+
(schema) => normalizePath(schema.path) !== KEYSTONE_WALLET_ID_PATH
|
|
1724
|
+
)
|
|
1853
1725
|
];
|
|
1854
1726
|
const requestUr = this.urEngine.buildKeyDerivationRequest({
|
|
1855
|
-
schemas: schemas.map((schema) => (
|
|
1856
|
-
path: schema.path,
|
|
1857
|
-
curve: schema.hwkChain === "sol" ? "ed25519" : "secp256k1"
|
|
1858
|
-
})),
|
|
1727
|
+
schemas: schemas.map((schema) => keySchema(schema.hwkChain, schema.path)),
|
|
1859
1728
|
origin: this._origin
|
|
1860
1729
|
});
|
|
1861
1730
|
const responseUr = await this._requestQrDisplayAndAwaitResponse(
|
|
1862
1731
|
record ? toDeviceInfo(record) : placeholderDeviceInfo(),
|
|
1863
|
-
{ ...requestUr, animated: false }
|
|
1732
|
+
{ ...requestUr, animated: false },
|
|
1733
|
+
signal
|
|
1864
1734
|
);
|
|
1865
1735
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
1866
1736
|
const parsed = this.urEngine.parseAccountResponse(responseUr);
|
|
@@ -1873,20 +1743,10 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1873
1743
|
const normalizedPath = normalizePath(account.path);
|
|
1874
1744
|
const hwkChain = requestedChainByPath.get(normalizedPath) ?? inferHwkChainFromPath(normalizedPath);
|
|
1875
1745
|
if (hwkChain) {
|
|
1876
|
-
book.set(accountKey(hwkChain, normalizedPath), {
|
|
1877
|
-
...account,
|
|
1878
|
-
hwkChain
|
|
1879
|
-
});
|
|
1880
|
-
}
|
|
1881
|
-
}
|
|
1882
|
-
for (const schema of missingSchemas) {
|
|
1883
|
-
if (!book.has(accountKey(schema.hwkChain, schema.path))) {
|
|
1884
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1885
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
|
|
1886
|
-
message: `Keystone did not return the requested derivation path (${schema.path})`
|
|
1887
|
-
});
|
|
1746
|
+
book.set(accountKey(hwkChain, normalizedPath), { ...account, hwkChain });
|
|
1888
1747
|
}
|
|
1889
1748
|
}
|
|
1749
|
+
assertAllBooked();
|
|
1890
1750
|
return { book, walletId: record.walletId };
|
|
1891
1751
|
});
|
|
1892
1752
|
}
|
|
@@ -1895,31 +1755,28 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1895
1755
|
* fingerprint is protocol metadata and is never accepted as a lookup key.
|
|
1896
1756
|
*/
|
|
1897
1757
|
_resolveTarget(connectId, deviceId) {
|
|
1898
|
-
|
|
1899
|
-
if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId)) {
|
|
1900
|
-
resolvedConnectId = this._operations.resolve(connectId).connectId;
|
|
1901
|
-
}
|
|
1758
|
+
const resolvedConnectId = (0, import_hwk_adapter_core4.isHardwareOperationId)(connectId) ? this._operations.resolve(connectId).connectId : connectId;
|
|
1902
1759
|
const identifiers = [deviceId, resolvedConnectId].filter(
|
|
1903
1760
|
(identifier) => Boolean(identifier)
|
|
1904
1761
|
);
|
|
1905
1762
|
if (!identifiers.length) return {};
|
|
1906
|
-
const walletIds = identifiers.map((identifier) =>
|
|
1763
|
+
const walletIds = identifiers.map((identifier) => keystoneWalletIdFromIdentifier(identifier)).filter((walletId) => Boolean(walletId));
|
|
1907
1764
|
if (!walletIds.length) {
|
|
1908
|
-
throw (0,
|
|
1909
|
-
code:
|
|
1765
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1766
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceNotFound,
|
|
1910
1767
|
message: "Keystone device lookup requires a 64-hex wallet identity"
|
|
1911
1768
|
});
|
|
1912
1769
|
}
|
|
1913
1770
|
const expectedWalletId = walletIds[0];
|
|
1914
1771
|
if (walletIds.some((walletId) => walletId !== expectedWalletId)) {
|
|
1915
|
-
throw (0,
|
|
1916
|
-
code:
|
|
1772
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1773
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch,
|
|
1917
1774
|
message: "Keystone connectId and deviceId identify different wallets"
|
|
1918
1775
|
});
|
|
1919
1776
|
}
|
|
1920
|
-
if (identifiers.some((identifier) => !
|
|
1921
|
-
throw (0,
|
|
1922
|
-
code:
|
|
1777
|
+
if (identifiers.some((identifier) => !keystoneWalletIdFromIdentifier(identifier))) {
|
|
1778
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1779
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch,
|
|
1923
1780
|
message: "Keystone connectId and deviceId are inconsistent"
|
|
1924
1781
|
});
|
|
1925
1782
|
}
|
|
@@ -1933,27 +1790,22 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1933
1790
|
_assertParsedIdentity(parsed, expected) {
|
|
1934
1791
|
const walletId = deriveKeystoneWalletId(parsed.accounts);
|
|
1935
1792
|
if (expected.expectedMasterFingerprint && parsed.masterFingerprint !== expected.expectedMasterFingerprint) {
|
|
1936
|
-
throw (0,
|
|
1937
|
-
code:
|
|
1793
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1794
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch,
|
|
1938
1795
|
message: `Connected Keystone wallet (mfp ${parsed.masterFingerprint}) does not match the requested wallet fingerprint (${expected.expectedMasterFingerprint})`
|
|
1939
1796
|
});
|
|
1940
1797
|
}
|
|
1941
1798
|
if (expected.expectedWalletId && walletId !== expected.expectedWalletId) {
|
|
1942
|
-
throw (0,
|
|
1943
|
-
code:
|
|
1799
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1800
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch,
|
|
1944
1801
|
message: "Connected Keystone wallet does not match the requested wallet identity"
|
|
1945
1802
|
});
|
|
1946
1803
|
}
|
|
1947
1804
|
return walletId;
|
|
1948
1805
|
}
|
|
1949
1806
|
/**
|
|
1950
|
-
* Folds a parsed account
|
|
1951
|
-
*
|
|
1952
|
-
* `_resolveUr` routes a KeyDerivation sync over USB when the target record
|
|
1953
|
-
* already has a live session, so this must NOT unconditionally mark
|
|
1954
|
-
* `qrSynced`, or a USB-only wallet would wrongly survive a later USB
|
|
1955
|
-
* disconnect as a "QR-synced, demote to QR-only" entry instead of being
|
|
1956
|
-
* dropped outright (see `releaseOperation`).
|
|
1807
|
+
* Folds a parsed account response into the device table. Only non-USB round trips mark
|
|
1808
|
+
* `qrSynced`, so a USB-only wallet is dropped on disconnect.
|
|
1957
1809
|
*/
|
|
1958
1810
|
_upsertDeviceRecord(parsed, options) {
|
|
1959
1811
|
const walletId = deriveKeystoneWalletId(parsed.accounts);
|
|
@@ -1963,8 +1815,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1963
1815
|
record = createDeviceRecord(walletId, parsed.masterFingerprint);
|
|
1964
1816
|
this._devices.set(walletId, record);
|
|
1965
1817
|
} else if (record.masterFingerprint !== parsed.masterFingerprint) {
|
|
1966
|
-
throw (0,
|
|
1967
|
-
code:
|
|
1818
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1819
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch,
|
|
1968
1820
|
message: "Keystone wallet identity returned an inconsistent master fingerprint"
|
|
1969
1821
|
});
|
|
1970
1822
|
}
|
|
@@ -1974,42 +1826,46 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
1974
1826
|
record.usbSessionId = options?.usbSessionId ?? record.usbSessionId;
|
|
1975
1827
|
if (!options?.viaUsb) record.qrSynced = true;
|
|
1976
1828
|
const info = toDeviceInfo(record);
|
|
1977
|
-
const eventType = isNew ?
|
|
1829
|
+
const eventType = isNew ? import_hwk_adapter_core4.DEVICE.CONNECT : import_hwk_adapter_core4.DEVICE.CHANGED;
|
|
1978
1830
|
this.emitter.emit(eventType, { type: eventType, payload: info });
|
|
1979
1831
|
return record;
|
|
1980
1832
|
}
|
|
1981
1833
|
/**
|
|
1982
|
-
* Opens
|
|
1983
|
-
*
|
|
1984
|
-
* one fixed account-level xpub and derives the stable wallet id. A
|
|
1985
|
-
* QR-synced entry becomes
|
|
1986
|
-
* `{qr, usb}`-capable in place (one `device-changed`, not a second
|
|
1987
|
-
* `device-connect`); a wallet never seen before becomes a new USB-only
|
|
1988
|
-
* entry. See §4.2 of the design doc.
|
|
1834
|
+
* Opens a permitted USB Keystone, reads its mfp, and derives the wallet id from the identity
|
|
1835
|
+
* xpub. A known wallet gains USB in place (`device-changed`); an unseen one becomes USB-only.
|
|
1989
1836
|
*/
|
|
1990
|
-
async _connectUsb(expected, searchTargetId) {
|
|
1991
|
-
if (this._pendingUsbTeardowns > 0) {
|
|
1992
|
-
return (0,
|
|
1993
|
-
|
|
1837
|
+
async _connectUsb(expected, searchTargetId, signal) {
|
|
1838
|
+
if (this._pendingUsbTeardowns > 0 || this._abandonedUsbConnects > 0) {
|
|
1839
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1840
|
+
import_hwk_adapter_core4.HardwareErrorCode.DeviceBusyInternal,
|
|
1994
1841
|
"Keystone USB session is being released"
|
|
1995
1842
|
);
|
|
1996
1843
|
}
|
|
1997
|
-
|
|
1998
|
-
let
|
|
1999
|
-
|
|
2000
|
-
|
|
1844
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
1845
|
+
let abandoned = false;
|
|
1846
|
+
const onAbort = () => {
|
|
1847
|
+
abandoned = true;
|
|
1848
|
+
this._abandonedUsbConnects += 1;
|
|
1849
|
+
};
|
|
1850
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1851
|
+
const pending = this._usbConnectTail.then(async () => {
|
|
1852
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
1853
|
+
return this._connectUsbExclusive(expected, searchTargetId, signal);
|
|
2001
1854
|
});
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
}
|
|
2006
|
-
|
|
2007
|
-
|
|
1855
|
+
const settled = pending.finally(() => {
|
|
1856
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1857
|
+
if (abandoned) this._abandonedUsbConnects -= 1;
|
|
1858
|
+
});
|
|
1859
|
+
this._usbConnectTail = settled.then(
|
|
1860
|
+
() => void 0,
|
|
1861
|
+
() => void 0
|
|
1862
|
+
);
|
|
1863
|
+
return signal ? _KeystoneAdapter._abortable(signal, settled) : settled;
|
|
2008
1864
|
}
|
|
2009
|
-
async _connectUsbExclusive(expected, searchTargetId) {
|
|
1865
|
+
async _connectUsbExclusive(expected, searchTargetId, signal) {
|
|
2010
1866
|
if (!this._usbConnector) {
|
|
2011
|
-
return (0,
|
|
2012
|
-
|
|
1867
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
1868
|
+
import_hwk_adapter_core4.HardwareErrorCode.TransportNotAvailable,
|
|
2013
1869
|
"No USB connector configured for this Keystone adapter"
|
|
2014
1870
|
);
|
|
2015
1871
|
}
|
|
@@ -2027,23 +1883,24 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2027
1883
|
const connector = this._usbConnector;
|
|
2028
1884
|
const session = await (connector.connectTarget ? connector.connectTarget(connectTarget) : connector.connect(searchTargetId ?? expected.expectedMasterFingerprint));
|
|
2029
1885
|
sessionId = session.sessionId;
|
|
1886
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
2030
1887
|
const raw = session.deviceInfo.raw;
|
|
2031
|
-
const mfpValue = (0,
|
|
1888
|
+
const mfpValue = (0, import_hwk_adapter_core4.parseBip32MasterFingerprint)(raw?.masterFingerprint);
|
|
2032
1889
|
if (!mfpValue) {
|
|
2033
|
-
throw (0,
|
|
2034
|
-
code:
|
|
1890
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1891
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch,
|
|
2035
1892
|
message: "Keystone USB did not report a master fingerprint"
|
|
2036
1893
|
});
|
|
2037
1894
|
}
|
|
2038
1895
|
const masterFingerprint = mfpValue.toLowerCase();
|
|
2039
1896
|
if (expected.expectedMasterFingerprint && masterFingerprint !== expected.expectedMasterFingerprint) {
|
|
2040
|
-
throw (0,
|
|
2041
|
-
code:
|
|
1897
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1898
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch,
|
|
2042
1899
|
message: `Connected Keystone wallet (mfp ${masterFingerprint}) does not match the requested wallet fingerprint (${expected.expectedMasterFingerprint})`
|
|
2043
1900
|
});
|
|
2044
1901
|
}
|
|
2045
1902
|
const identityRequest = this.urEngine.buildKeyDerivationRequest({
|
|
2046
|
-
schemas: [
|
|
1903
|
+
schemas: [keySchema("evm", KEYSTONE_WALLET_ID_PATH)],
|
|
2047
1904
|
origin: this._origin
|
|
2048
1905
|
});
|
|
2049
1906
|
const identityResult = await this._callUsbConnector(
|
|
@@ -2051,8 +1908,9 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2051
1908
|
"resolveUr",
|
|
2052
1909
|
identityRequest
|
|
2053
1910
|
);
|
|
1911
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
2054
1912
|
if (!identityResult.success) {
|
|
2055
|
-
throw (0,
|
|
1913
|
+
throw (0, import_hwk_adapter_core4.rehydrateConnectorError)(identityResult.error);
|
|
2056
1914
|
}
|
|
2057
1915
|
const parsed = this.urEngine.parseAccountResponse(identityResult.payload);
|
|
2058
1916
|
this._assertParsedIdentity(parsed, {
|
|
@@ -2061,21 +1919,22 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2061
1919
|
});
|
|
2062
1920
|
const walletId = deriveKeystoneWalletId(parsed.accounts);
|
|
2063
1921
|
const previousUsbSessionId = this._devices.get(walletId)?.usbSessionId;
|
|
2064
|
-
const record = this._upsertDeviceRecord(parsed, {
|
|
2065
|
-
viaUsb: true,
|
|
2066
|
-
usbSessionId: session.sessionId
|
|
2067
|
-
});
|
|
2068
1922
|
if (previousUsbSessionId && previousUsbSessionId !== session.sessionId) {
|
|
2069
1923
|
try {
|
|
2070
1924
|
await this._usbConnector.disconnect(previousUsbSessionId);
|
|
2071
1925
|
} catch {
|
|
2072
1926
|
}
|
|
2073
1927
|
}
|
|
1928
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
1929
|
+
const record = this._upsertDeviceRecord(parsed, {
|
|
1930
|
+
viaUsb: true,
|
|
1931
|
+
usbSessionId: session.sessionId
|
|
1932
|
+
});
|
|
1933
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
2074
1934
|
record.hadUsbSession = true;
|
|
2075
1935
|
record.model = session.deviceInfo.modelName ?? session.deviceInfo.model ?? record.model;
|
|
2076
1936
|
record.deviceVersion = session.deviceInfo.firmwareVersion ?? record.deviceVersion;
|
|
2077
|
-
|
|
2078
|
-
return (0, import_hwk_adapter_core3.success)(info);
|
|
1937
|
+
return (0, import_hwk_adapter_core4.success)(toDeviceInfo(record));
|
|
2079
1938
|
} catch (err) {
|
|
2080
1939
|
if (sessionId) {
|
|
2081
1940
|
try {
|
|
@@ -2087,13 +1946,11 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2087
1946
|
}
|
|
2088
1947
|
}
|
|
2089
1948
|
/**
|
|
2090
|
-
*
|
|
2091
|
-
*
|
|
2092
|
-
* used both after an adapter restart and after a QR-only period, so plugging
|
|
2093
|
-
* USB back in affects the next business call without changing an in-flight
|
|
2094
|
-
* QR operation.
|
|
1949
|
+
* Probes already-authorized USB devices without a permission picker, then attaches and verifies
|
|
1950
|
+
* the requested wallet if present.
|
|
2095
1951
|
*/
|
|
2096
|
-
async _tryUsbAttach(expectedWalletId, expectedMasterFingerprint, options) {
|
|
1952
|
+
async _tryUsbAttach(signal, expectedWalletId, expectedMasterFingerprint, options) {
|
|
1953
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
2097
1954
|
if (!expectedWalletId || this._forcedTransport === "qr" || !this._usbConnector) {
|
|
2098
1955
|
return void 0;
|
|
2099
1956
|
}
|
|
@@ -2101,17 +1958,22 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2101
1958
|
let availableDevices = [];
|
|
2102
1959
|
let lastSearchError;
|
|
2103
1960
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
1961
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
2104
1962
|
try {
|
|
2105
|
-
availableDevices = await
|
|
1963
|
+
availableDevices = await _KeystoneAdapter._abortable(
|
|
1964
|
+
signal,
|
|
1965
|
+
this._usbConnector.searchDevices({ purpose: "availability" })
|
|
1966
|
+
);
|
|
2106
1967
|
lastSearchError = void 0;
|
|
2107
1968
|
} catch (error) {
|
|
2108
1969
|
lastSearchError = error;
|
|
2109
1970
|
}
|
|
1971
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
2110
1972
|
if (availableDevices.length) {
|
|
2111
1973
|
break;
|
|
2112
1974
|
}
|
|
2113
1975
|
if (attempt < maxAttempts) {
|
|
2114
|
-
await waitForKeystoneUsbReattachProbe();
|
|
1976
|
+
await _KeystoneAdapter._abortable(signal, waitForKeystoneUsbReattachProbe());
|
|
2115
1977
|
}
|
|
2116
1978
|
}
|
|
2117
1979
|
if (!availableDevices.length) {
|
|
@@ -2126,8 +1988,16 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2126
1988
|
for (const device of candidates) {
|
|
2127
1989
|
attached = await this._connectUsb(
|
|
2128
1990
|
{ expectedWalletId, expectedMasterFingerprint },
|
|
2129
|
-
device?.connectId
|
|
1991
|
+
device?.connectId,
|
|
1992
|
+
signal
|
|
2130
1993
|
);
|
|
1994
|
+
_KeystoneAdapter._throwIfAborted(signal);
|
|
1995
|
+
if (!attached.success && attached.payload.code === import_hwk_adapter_core4.HardwareErrorCode.DeviceBusyInternal) {
|
|
1996
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
1997
|
+
...attached.payload,
|
|
1998
|
+
message: attached.payload.error
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2131
2001
|
if (attached.success) {
|
|
2132
2002
|
record = this._devices.get(expectedWalletId);
|
|
2133
2003
|
if (record) break;
|
|
@@ -2136,17 +2006,11 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2136
2006
|
return record;
|
|
2137
2007
|
}
|
|
2138
2008
|
/**
|
|
2139
|
-
*
|
|
2140
|
-
*
|
|
2141
|
-
* device row for the target wallet. USB is the preferred channel: a known
|
|
2142
|
-
* wallet record with no live session gets ONE best-effort re-attach here,
|
|
2143
|
-
* and anything that goes wrong just leaves the call on QR. A
|
|
2144
|
-
* `switchTransport('qr')` pin forces QR even for a USB-attached wallet;
|
|
2145
|
-
* `switchTransport('usb')` on a wallet with no live USB session fails
|
|
2146
|
-
* closed rather than silently falling back to QR.
|
|
2009
|
+
* Decides QR vs USB for one UR round trip. A pinned 'usb' with no live session fails closed;
|
|
2010
|
+
* otherwise a failed USB reattach leaves the call on QR.
|
|
2147
2011
|
*/
|
|
2148
2012
|
async _resolveUr(record, requestUr, animated, operationId, signal, operationName) {
|
|
2149
|
-
const releaseOperationRetention = (0,
|
|
2013
|
+
const releaseOperationRetention = (0, import_hwk_adapter_core4.isHardwareOperationId)(operationId) ? this._operations.retain(operationId) : void 0;
|
|
2150
2014
|
try {
|
|
2151
2015
|
return await this._resolveUrWithRoute(
|
|
2152
2016
|
record,
|
|
@@ -2162,21 +2026,19 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2162
2026
|
}
|
|
2163
2027
|
async _resolveUrWithRoute(record, requestUr, animated, operationId, signal, operationName) {
|
|
2164
2028
|
let interactionRoute;
|
|
2165
|
-
if ((0,
|
|
2029
|
+
if ((0, import_hwk_adapter_core4.isHardwareOperationId)(operationId)) {
|
|
2166
2030
|
const operation = this._operations.resolve(operationId);
|
|
2167
2031
|
const route = this._operationRoutes.get(operation.connectId);
|
|
2168
2032
|
if (!route || route.operationId !== operationId) {
|
|
2169
|
-
this.
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
params: { operationId, reason: "disconnect" }
|
|
2174
|
-
});
|
|
2033
|
+
throw this._endDisconnectedOperation(
|
|
2034
|
+
operationId,
|
|
2035
|
+
"Keystone hardware operation is no longer connected"
|
|
2036
|
+
);
|
|
2175
2037
|
}
|
|
2176
2038
|
interactionRoute = route;
|
|
2177
2039
|
}
|
|
2178
2040
|
if (!interactionRoute && !this._forcedTransport && record && !record.usbSessionId && this._usbConnector) {
|
|
2179
|
-
await this._tryUsbAttach(record.walletId, record.masterFingerprint, {
|
|
2041
|
+
await this._tryUsbAttach(signal, record.walletId, record.masterFingerprint, {
|
|
2180
2042
|
waitForReenumeration: record.hadUsbSession
|
|
2181
2043
|
});
|
|
2182
2044
|
}
|
|
@@ -2184,18 +2046,13 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2184
2046
|
if (wantUsb) {
|
|
2185
2047
|
if (!record?.usbSessionId || !this._usbConnector) {
|
|
2186
2048
|
if (interactionRoute) {
|
|
2187
|
-
this.
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
params: {
|
|
2192
|
-
operationId: interactionRoute.operationId,
|
|
2193
|
-
reason: "disconnect"
|
|
2194
|
-
}
|
|
2195
|
-
});
|
|
2049
|
+
throw this._endDisconnectedOperation(
|
|
2050
|
+
interactionRoute.operationId,
|
|
2051
|
+
"Keystone operation USB connection was lost"
|
|
2052
|
+
);
|
|
2196
2053
|
}
|
|
2197
|
-
throw (0,
|
|
2198
|
-
code:
|
|
2054
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
2055
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.TransportNotAvailable,
|
|
2199
2056
|
message: "USB channel is not connected for this Keystone wallet"
|
|
2200
2057
|
});
|
|
2201
2058
|
}
|
|
@@ -2206,17 +2063,20 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2206
2063
|
signal
|
|
2207
2064
|
);
|
|
2208
2065
|
if (result.success) return result.payload;
|
|
2209
|
-
const usbError = (0,
|
|
2210
|
-
const usbErrorOrigin = usbError
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2066
|
+
const usbError = (0, import_hwk_adapter_core4.rehydrateConnectorError)(result.error);
|
|
2067
|
+
const { origin: usbErrorOrigin, code: usbErrorCode } = usbError;
|
|
2068
|
+
if (usbErrorCode === import_hwk_adapter_core4.HardwareErrorCode.PayloadTooLarge && usbErrorOrigin !== "device") {
|
|
2069
|
+
if (!interactionRoute && !this._forcedTransport) {
|
|
2070
|
+
const displayDevice2 = toDeviceInfo(record);
|
|
2071
|
+
return this._requestQrDisplayAndAwaitResponse(
|
|
2072
|
+
displayDevice2,
|
|
2073
|
+
{ ...requestUr, animated },
|
|
2074
|
+
signal
|
|
2075
|
+
);
|
|
2076
|
+
}
|
|
2077
|
+
throw usbError;
|
|
2218
2078
|
}
|
|
2219
|
-
const deviceAnswered = usbErrorOrigin !== void 0 ? usbErrorOrigin === "device" : usbErrorCode ===
|
|
2079
|
+
const deviceAnswered = usbErrorOrigin !== void 0 ? usbErrorOrigin === "device" : usbErrorCode === import_hwk_adapter_core4.HardwareErrorCode.UserRejected || usbErrorCode === import_hwk_adapter_core4.HardwareErrorCode.UserAborted || usbErrorCode === import_hwk_adapter_core4.HardwareErrorCode.DeviceLocked || usbErrorCode === import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch;
|
|
2220
2080
|
if (!deviceAnswered) {
|
|
2221
2081
|
const lostSessionId = record.usbSessionId;
|
|
2222
2082
|
if (lostSessionId) {
|
|
@@ -2224,27 +2084,19 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2224
2084
|
}
|
|
2225
2085
|
record.usbSessionId = void 0;
|
|
2226
2086
|
if (interactionRoute) {
|
|
2227
|
-
this.
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
params: operationName ? (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(operationName, {
|
|
2233
|
-
operationId: interactionRoute.operationId,
|
|
2234
|
-
reason: "disconnect"
|
|
2235
|
-
}) : {
|
|
2236
|
-
operationId: interactionRoute.operationId,
|
|
2237
|
-
reason: "disconnect"
|
|
2238
|
-
}
|
|
2239
|
-
});
|
|
2087
|
+
throw this._endDisconnectedOperation(
|
|
2088
|
+
interactionRoute.operationId,
|
|
2089
|
+
operationName ? `Keystone ${operationName} may have completed before the USB connection was lost` : "Keystone operation USB connection was lost",
|
|
2090
|
+
operationName
|
|
2091
|
+
);
|
|
2240
2092
|
}
|
|
2241
2093
|
if (operationName) {
|
|
2242
|
-
throw (0,
|
|
2243
|
-
code: usbErrorCode ??
|
|
2094
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
2095
|
+
code: usbErrorCode ?? import_hwk_adapter_core4.HardwareErrorCode.TransportError,
|
|
2244
2096
|
message: `Keystone ${operationName} may have completed before the USB connection was lost`,
|
|
2245
2097
|
origin: "transport",
|
|
2246
2098
|
recovery: { scope: "unknown" },
|
|
2247
|
-
params: (0,
|
|
2099
|
+
params: (0, import_hwk_adapter_core4.operationMayHaveCompletedParams)(operationName)
|
|
2248
2100
|
});
|
|
2249
2101
|
}
|
|
2250
2102
|
}
|
|
@@ -2252,71 +2104,70 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2252
2104
|
}
|
|
2253
2105
|
const displayDevice = record ? toDeviceInfo(record) : placeholderDeviceInfo();
|
|
2254
2106
|
try {
|
|
2255
|
-
return await this._requestQrDisplayAndAwaitResponse(
|
|
2256
|
-
|
|
2257
|
-
animated
|
|
2258
|
-
|
|
2107
|
+
return await this._requestQrDisplayAndAwaitResponse(
|
|
2108
|
+
displayDevice,
|
|
2109
|
+
{ ...requestUr, animated },
|
|
2110
|
+
signal
|
|
2111
|
+
);
|
|
2259
2112
|
} catch (error) {
|
|
2260
2113
|
const { code, _tag: tag } = error;
|
|
2261
|
-
if (operationName && (code ===
|
|
2262
|
-
throw (0,
|
|
2263
|
-
code:
|
|
2114
|
+
if (operationName && (code === import_hwk_adapter_core4.HardwareErrorCode.OperationTimeout || tag === import_hwk_adapter_core4.UI_REQUEST_TIMEOUT_TAG)) {
|
|
2115
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
2116
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.OperationTimeout,
|
|
2264
2117
|
message: `Keystone ${operationName} may have completed before the QR response timed out`,
|
|
2265
2118
|
recovery: { scope: "unknown" },
|
|
2266
|
-
params: (0,
|
|
2119
|
+
params: (0, import_hwk_adapter_core4.operationMayHaveCompletedParams)(operationName)
|
|
2267
2120
|
});
|
|
2268
2121
|
}
|
|
2269
2122
|
throw error;
|
|
2270
2123
|
}
|
|
2271
2124
|
}
|
|
2125
|
+
/** Ends the operation as disconnected and returns the error to throw. */
|
|
2126
|
+
_endDisconnectedOperation(operationId, message, operationName) {
|
|
2127
|
+
this._operations.end(operationId, "disconnect");
|
|
2128
|
+
const params = { operationId, reason: "disconnect" };
|
|
2129
|
+
return (0, import_hwk_adapter_core4.createHwkError)({
|
|
2130
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.OperationEnded,
|
|
2131
|
+
message,
|
|
2132
|
+
recovery: operationName ? { scope: "unknown" } : void 0,
|
|
2133
|
+
params: operationName ? (0, import_hwk_adapter_core4.operationMayHaveCompletedParams)(operationName, params) : params
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2272
2136
|
/**
|
|
2273
|
-
*
|
|
2274
|
-
*
|
|
2275
|
-
* retained on the record. A failed USB export ends this operation without
|
|
2276
|
-
* replaying the request or switching transports.
|
|
2137
|
+
* Fetches one account's key material for this operation only; `book` reuses earlier fetches. A
|
|
2138
|
+
* failed USB export ends the operation without replaying or switching transports.
|
|
2277
2139
|
*/
|
|
2278
|
-
async _fetchAccount(connectId, deviceId, hwkChain, syncPath, signal,
|
|
2140
|
+
async _fetchAccount(connectId, deviceId, hwkChain, syncPath, signal, book) {
|
|
2279
2141
|
const target = this._resolveTarget(connectId, deviceId);
|
|
2142
|
+
const operationConnectionType = (0, import_hwk_adapter_core4.isHardwareOperationId)(connectId) ? this._operations.resolve(connectId).connectionType : void 0;
|
|
2280
2143
|
const key = accountKey(hwkChain, syncPath);
|
|
2281
2144
|
let existingRecord = target.record;
|
|
2282
2145
|
if (!existingRecord) {
|
|
2283
|
-
|
|
2146
|
+
existingRecord = await this._tryUsbAttach(signal, target.expectedWalletId);
|
|
2284
2147
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
2285
|
-
existingRecord = attached;
|
|
2286
2148
|
}
|
|
2287
|
-
const book = options?.book;
|
|
2288
2149
|
const booked = book?.get(key);
|
|
2289
2150
|
if (existingRecord && booked) {
|
|
2290
2151
|
return { record: existingRecord, account: booked };
|
|
2291
2152
|
}
|
|
2292
|
-
if (existingRecord && !existingRecord.usbSessionId && this._forcedTransport === "usb") {
|
|
2293
|
-
await this._tryUsbAttach(existingRecord.walletId, existingRecord.masterFingerprint, {
|
|
2153
|
+
if (!operationConnectionType && existingRecord && !existingRecord.usbSessionId && this._forcedTransport === "usb") {
|
|
2154
|
+
await this._tryUsbAttach(signal, existingRecord.walletId, existingRecord.masterFingerprint, {
|
|
2294
2155
|
waitForReenumeration: existingRecord.hadUsbSession
|
|
2295
2156
|
});
|
|
2296
2157
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
2297
2158
|
existingRecord = this._devices.get(existingRecord.walletId) ?? existingRecord;
|
|
2298
2159
|
}
|
|
2299
|
-
const useSinglePathUsbExport = this._forcedTransport !== "qr" && Boolean(existingRecord?.usbSessionId);
|
|
2300
|
-
const
|
|
2301
|
-
|
|
2302
|
-
];
|
|
2303
|
-
const schemaPaths = useSinglePathUsbExport || requestedSchemaPaths.some((schema) => normalizePath(schema.path) === KEYSTONE_WALLET_ID_PATH) ? requestedSchemaPaths : [{ hwkChain: "evm", path: KEYSTONE_WALLET_ID_PATH }, ...requestedSchemaPaths];
|
|
2304
|
-
const requestUr = this.urEngine.buildKeyDerivationRequest({
|
|
2305
|
-
schemas: schemaPaths.map((s) => ({
|
|
2306
|
-
path: s.path,
|
|
2307
|
-
curve: s.hwkChain === "sol" ? "ed25519" : "secp256k1"
|
|
2308
|
-
})),
|
|
2309
|
-
origin: this._origin
|
|
2310
|
-
});
|
|
2311
|
-
const resolvedViaUsb = useSinglePathUsbExport;
|
|
2160
|
+
const useSinglePathUsbExport = operationConnectionType ? operationConnectionType === "usb" : this._forcedTransport !== "qr" && Boolean(existingRecord?.usbSessionId);
|
|
2161
|
+
const schemas = useSinglePathUsbExport || normalizePath(syncPath) === KEYSTONE_WALLET_ID_PATH ? [keySchema(hwkChain, syncPath)] : [keySchema("evm", KEYSTONE_WALLET_ID_PATH), keySchema(hwkChain, syncPath)];
|
|
2162
|
+
const requestUr = this.urEngine.buildKeyDerivationRequest({ schemas, origin: this._origin });
|
|
2312
2163
|
const responseUr = await this._resolveUr(existingRecord, requestUr, false, connectId, signal);
|
|
2313
2164
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
2314
2165
|
const parsed = this.urEngine.parseAccountResponse(responseUr);
|
|
2315
2166
|
let record;
|
|
2316
|
-
if (
|
|
2167
|
+
if (useSinglePathUsbExport && existingRecord) {
|
|
2317
2168
|
if (parsed.masterFingerprint !== existingRecord.masterFingerprint) {
|
|
2318
|
-
throw (0,
|
|
2319
|
-
code:
|
|
2169
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
2170
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch,
|
|
2320
2171
|
message: "Keystone USB account export returned a different master fingerprint"
|
|
2321
2172
|
});
|
|
2322
2173
|
}
|
|
@@ -2324,13 +2175,13 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2324
2175
|
} else {
|
|
2325
2176
|
this._assertParsedIdentity(parsed, target);
|
|
2326
2177
|
record = this._upsertDeviceRecord(parsed, {
|
|
2327
|
-
viaUsb: Boolean(existingRecord?.usbSessionId)
|
|
2178
|
+
viaUsb: operationConnectionType !== "qr" && Boolean(existingRecord?.usbSessionId)
|
|
2328
2179
|
});
|
|
2329
2180
|
}
|
|
2330
2181
|
const account = parsed.accounts.find((a) => normalizePath(a.path) === syncPath);
|
|
2331
2182
|
if (!account) {
|
|
2332
|
-
throw (0,
|
|
2333
|
-
code:
|
|
2183
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
2184
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch,
|
|
2334
2185
|
message: `Keystone did not return the requested derivation path (${syncPath})`
|
|
2335
2186
|
});
|
|
2336
2187
|
}
|
|
@@ -2350,88 +2201,115 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2350
2201
|
return { record, account: entry };
|
|
2351
2202
|
}
|
|
2352
2203
|
/**
|
|
2353
|
-
*
|
|
2354
|
-
*
|
|
2355
|
-
* specific cached path. Syncs the account-level path for `chain` as a
|
|
2356
|
-
* throwaway probe when the wallet record isn't already known.
|
|
2357
|
-
*
|
|
2358
|
-
* `CHAIN_FINGERPRINT_PATHS[chain]` is a 5-segment LEAF path for `evm`
|
|
2359
|
-
* (`m/44'/60'/0'/0/0`) — sending that verbatim as a KeyDerivation request
|
|
2360
|
-
* asks Keystone for a non-standard path. Keystone's own docs
|
|
2361
|
-
* (dev.keyst.one's multichain KeyDerivation example) show the ETH
|
|
2362
|
-
* account-level path as `m/44'/60'/0'` (3 segments), same as what
|
|
2363
|
-
* `_fetchAccount` already requests — so
|
|
2364
|
-
* truncate through `splitAccountPath` here too instead of using the raw
|
|
2365
|
-
* fingerprint leaf path. `btc`/`sol` are already 3-segment account paths
|
|
2366
|
-
* and pass through unchanged.
|
|
2204
|
+
* Resolves which wallet is attached, without a per-path account. `CHAIN_FINGERPRINT_PATHS.evm` is
|
|
2205
|
+
* a leaf path, so it is cut to the account path Keystone expects.
|
|
2367
2206
|
*/
|
|
2368
2207
|
async _ensureWalletKnown(connectId, deviceId, chain, signal) {
|
|
2369
2208
|
const target = this._resolveTarget(connectId, deviceId);
|
|
2370
2209
|
if (target.record) return { record: target.record };
|
|
2371
|
-
const attached = await this._tryUsbAttach(target.expectedWalletId);
|
|
2210
|
+
const attached = await this._tryUsbAttach(signal, target.expectedWalletId);
|
|
2372
2211
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
2373
2212
|
if (attached) return { record: attached };
|
|
2374
|
-
const { accountPath } = splitAccountPath(
|
|
2375
|
-
const schemas = accountPath === KEYSTONE_WALLET_ID_PATH ? [
|
|
2376
|
-
|
|
2377
|
-
{
|
|
2378
|
-
path: accountPath,
|
|
2379
|
-
curve: chain === "sol" ? "ed25519" : "secp256k1"
|
|
2380
|
-
}
|
|
2381
|
-
];
|
|
2382
|
-
const requestUr = this.urEngine.buildKeyDerivationRequest({
|
|
2383
|
-
schemas,
|
|
2384
|
-
origin: this._origin
|
|
2385
|
-
});
|
|
2213
|
+
const { accountPath } = splitAccountPath(import_hwk_adapter_core4.CHAIN_FINGERPRINT_PATHS[chain]);
|
|
2214
|
+
const schemas = accountPath === KEYSTONE_WALLET_ID_PATH ? [keySchema("evm", KEYSTONE_WALLET_ID_PATH)] : [keySchema("evm", KEYSTONE_WALLET_ID_PATH), keySchema(chain, accountPath)];
|
|
2215
|
+
const requestUr = this.urEngine.buildKeyDerivationRequest({ schemas, origin: this._origin });
|
|
2386
2216
|
const responseUr = await this._resolveUr(void 0, requestUr, false, connectId, signal);
|
|
2387
2217
|
_KeystoneAdapter._throwIfAborted(signal);
|
|
2388
2218
|
const parsed = this.urEngine.parseAccountResponse(responseUr);
|
|
2389
2219
|
this._assertParsedIdentity(parsed, target);
|
|
2390
|
-
|
|
2391
|
-
|
|
2220
|
+
return { record: this._upsertDeviceRecord(parsed) };
|
|
2221
|
+
}
|
|
2222
|
+
/**
|
|
2223
|
+
* The operation owning the running job, so a mid-call UI request can name it. Unpinned jobs map
|
|
2224
|
+
* through the operation route; a cold start returns undefined.
|
|
2225
|
+
*/
|
|
2226
|
+
_activeOperationId() {
|
|
2227
|
+
const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
|
|
2228
|
+
if (!activeJobId) return void 0;
|
|
2229
|
+
if ((0, import_hwk_adapter_core4.isHardwareOperationId)(activeJobId)) return activeJobId;
|
|
2230
|
+
return this._operationRouteForIdentifier(activeJobId)?.operationId;
|
|
2231
|
+
}
|
|
2232
|
+
/** Routes are keyed by connectId; a job key may be the bare wallet id. */
|
|
2233
|
+
_operationRouteForIdentifier(identifier) {
|
|
2234
|
+
for (const key of keystoneCancelQueueKeys([identifier])) {
|
|
2235
|
+
const route = this._operationRoutes.get(key);
|
|
2236
|
+
if (route) return route;
|
|
2237
|
+
}
|
|
2238
|
+
const walletId = keystoneWalletIdFromIdentifier(identifier);
|
|
2239
|
+
return walletId ? this._operationRoutes.get(walletConnectId(walletId)) : void 0;
|
|
2392
2240
|
}
|
|
2393
|
-
|
|
2241
|
+
/**
|
|
2242
|
+
* A QR prompt has no transport to interrupt; releasing the registry slot on abort is what lets
|
|
2243
|
+
* a cancel reach the wait before the QR timeout.
|
|
2244
|
+
*/
|
|
2245
|
+
async _awaitQrResponse(requestType, pending, signal) {
|
|
2246
|
+
if (!signal) return pending;
|
|
2247
|
+
let settled = false;
|
|
2248
|
+
const onAbort = () => {
|
|
2249
|
+
if (!settled) this._uiRegistry.cancel(requestType);
|
|
2250
|
+
};
|
|
2251
|
+
if (signal.aborted) onAbort();
|
|
2252
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
2253
|
+
try {
|
|
2254
|
+
return await pending;
|
|
2255
|
+
} finally {
|
|
2256
|
+
settled = true;
|
|
2257
|
+
signal.removeEventListener("abort", onAbort);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
async _requestQrDisplayAndAwaitResponse(device, data, signal) {
|
|
2261
|
+
const operationId = this._activeOperationId();
|
|
2394
2262
|
const waitPromise = this._uiRegistry.wait(
|
|
2395
|
-
|
|
2396
|
-
{ timeoutMs: this._qrTimeoutMs }
|
|
2263
|
+
import_hwk_adapter_core4.UI_REQUEST.REQUEST_QR_DISPLAY,
|
|
2264
|
+
{ timeoutMs: this._qrTimeoutMs, operationId }
|
|
2397
2265
|
);
|
|
2398
|
-
this.emitter.emit(
|
|
2399
|
-
type:
|
|
2400
|
-
payload: { device, data }
|
|
2266
|
+
this.emitter.emit(import_hwk_adapter_core4.UI_REQUEST.REQUEST_QR_DISPLAY, {
|
|
2267
|
+
type: import_hwk_adapter_core4.UI_REQUEST.REQUEST_QR_DISPLAY,
|
|
2268
|
+
payload: { device, data, operationId }
|
|
2401
2269
|
});
|
|
2402
|
-
const response = await
|
|
2270
|
+
const response = await this._awaitQrResponse(
|
|
2271
|
+
import_hwk_adapter_core4.UI_REQUEST.REQUEST_QR_DISPLAY,
|
|
2272
|
+
waitPromise,
|
|
2273
|
+
signal
|
|
2274
|
+
);
|
|
2403
2275
|
return { urType: response.urType, urData: response.urData };
|
|
2404
2276
|
}
|
|
2405
|
-
async _requestQrScanAndAwaitResponse(device) {
|
|
2277
|
+
async _requestQrScanAndAwaitResponse(device, signal) {
|
|
2278
|
+
const operationId = this._activeOperationId();
|
|
2406
2279
|
const waitPromise = this._uiRegistry.wait(
|
|
2407
|
-
|
|
2408
|
-
{ timeoutMs: this._qrTimeoutMs }
|
|
2280
|
+
import_hwk_adapter_core4.UI_REQUEST.REQUEST_QR_SCAN,
|
|
2281
|
+
{ timeoutMs: this._qrTimeoutMs, operationId }
|
|
2409
2282
|
);
|
|
2410
|
-
this.emitter.emit(
|
|
2411
|
-
type:
|
|
2412
|
-
payload: { device }
|
|
2283
|
+
this.emitter.emit(import_hwk_adapter_core4.UI_REQUEST.REQUEST_QR_SCAN, {
|
|
2284
|
+
type: import_hwk_adapter_core4.UI_REQUEST.REQUEST_QR_SCAN,
|
|
2285
|
+
payload: { device, operationId }
|
|
2413
2286
|
});
|
|
2414
|
-
const response = await waitPromise;
|
|
2287
|
+
const response = await this._awaitQrResponse(import_hwk_adapter_core4.UI_REQUEST.REQUEST_QR_SCAN, waitPromise, signal);
|
|
2415
2288
|
return { urType: response.urType, urData: response.urData };
|
|
2416
2289
|
}
|
|
2417
2290
|
async _callUsbConnector(sessionId, method, params, signal) {
|
|
2418
2291
|
if (!this._usbConnector) {
|
|
2419
|
-
throw (0,
|
|
2420
|
-
code:
|
|
2292
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
2293
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.TransportNotAvailable,
|
|
2421
2294
|
message: "No USB connector configured for this Keystone adapter"
|
|
2422
2295
|
});
|
|
2423
2296
|
}
|
|
2424
2297
|
if (this._pendingUsbTeardowns > 0 || this._unsettledUsbOperations.size > 0) {
|
|
2425
|
-
throw (0,
|
|
2426
|
-
code:
|
|
2298
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
2299
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceBusyInternal,
|
|
2427
2300
|
message: `Keystone USB is busy while calling ${method}`
|
|
2428
2301
|
});
|
|
2429
2302
|
}
|
|
2303
|
+
if (signal?.aborted) {
|
|
2304
|
+
throw signal.reason instanceof Error ? signal.reason : (0, import_hwk_adapter_core4.createHwkError)({
|
|
2305
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.UserAborted,
|
|
2306
|
+
message: "User aborted operation"
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2430
2309
|
const releaseOperation = this._retainUsbOperation(`call:${sessionId}`);
|
|
2431
2310
|
let rawCall;
|
|
2432
2311
|
try {
|
|
2433
|
-
|
|
2434
|
-
rawCall = connector.call(sessionId, method, params).finally(releaseOperation);
|
|
2312
|
+
rawCall = this._usbConnector.call(sessionId, method, params).finally(releaseOperation);
|
|
2435
2313
|
} catch (error) {
|
|
2436
2314
|
releaseOperation();
|
|
2437
2315
|
throw error;
|
|
@@ -2481,13 +2359,10 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2481
2359
|
};
|
|
2482
2360
|
}
|
|
2483
2361
|
static _abortable(signal, promise) {
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
}
|
|
2362
|
+
const abortReason = () => signal.reason instanceof Error ? signal.reason : new Error("Aborted");
|
|
2363
|
+
if (signal.aborted) return Promise.reject(abortReason());
|
|
2487
2364
|
return new Promise((resolve, reject) => {
|
|
2488
|
-
const onAbort = () =>
|
|
2489
|
-
reject(signal.reason instanceof Error ? signal.reason : new Error("Aborted"));
|
|
2490
|
-
};
|
|
2365
|
+
const onAbort = () => reject(abortReason());
|
|
2491
2366
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
2492
2367
|
promise.then(
|
|
2493
2368
|
(value) => {
|
|
@@ -2502,22 +2377,22 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2502
2377
|
});
|
|
2503
2378
|
}
|
|
2504
2379
|
static _throwIfAborted(signal) {
|
|
2505
|
-
if (signal
|
|
2380
|
+
if (signal?.aborted) {
|
|
2506
2381
|
throw signal.reason instanceof Error ? signal.reason : new Error("Operation aborted");
|
|
2507
2382
|
}
|
|
2508
2383
|
}
|
|
2509
2384
|
static _assertRequestIdMatches(expected, actual) {
|
|
2510
2385
|
if (actual && actual.toLowerCase() !== expected.toLowerCase()) {
|
|
2511
|
-
throw (0,
|
|
2512
|
-
code:
|
|
2386
|
+
throw (0, import_hwk_adapter_core4.createHwkError)({
|
|
2387
|
+
code: import_hwk_adapter_core4.HardwareErrorCode.DeviceMismatch,
|
|
2513
2388
|
message: "Keystone response requestId does not match the pending request \u2014 discarding a stale or unrelated scan"
|
|
2514
2389
|
});
|
|
2515
2390
|
}
|
|
2516
2391
|
}
|
|
2517
2392
|
_unsupported(method, reason) {
|
|
2518
2393
|
return Promise.resolve(
|
|
2519
|
-
(0,
|
|
2520
|
-
|
|
2394
|
+
(0, import_hwk_adapter_core4.failure)(
|
|
2395
|
+
import_hwk_adapter_core4.HardwareErrorCode.MethodNotSupported,
|
|
2521
2396
|
`KeystoneAdapter.${method} is not implemented yet: ${reason}`
|
|
2522
2397
|
)
|
|
2523
2398
|
);
|
|
@@ -2527,30 +2402,30 @@ var KeystoneAdapter = class _KeystoneAdapter {
|
|
|
2527
2402
|
const e = err;
|
|
2528
2403
|
if (typeof e.code === "number" && e.code >= 1e4 && e.code <= 99999) {
|
|
2529
2404
|
const { origin } = e;
|
|
2530
|
-
return (0,
|
|
2405
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
2531
2406
|
e.code,
|
|
2532
2407
|
e.message ?? "Unknown error",
|
|
2533
2408
|
e.params,
|
|
2534
2409
|
origin === "device" || origin === "transport" || origin === "host" ? origin : void 0,
|
|
2535
|
-
(0,
|
|
2410
|
+
(0, import_hwk_adapter_core4.isHwkRecoveryHint)(e.recovery) ? e.recovery : void 0
|
|
2536
2411
|
);
|
|
2537
2412
|
}
|
|
2538
|
-
if (e._tag ===
|
|
2539
|
-
return (0,
|
|
2540
|
-
|
|
2413
|
+
if (e._tag === import_hwk_adapter_core4.UI_REQUEST_CANCELLED_TAG || e._tag === import_hwk_adapter_core4.UI_REQUEST_PREEMPTED_TAG) {
|
|
2414
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
2415
|
+
import_hwk_adapter_core4.HardwareErrorCode.UserAborted,
|
|
2541
2416
|
e.message ?? "Keystone QR operation was cancelled"
|
|
2542
2417
|
);
|
|
2543
2418
|
}
|
|
2544
|
-
if (e._tag ===
|
|
2545
|
-
return (0,
|
|
2546
|
-
|
|
2419
|
+
if (e._tag === import_hwk_adapter_core4.UI_REQUEST_TIMEOUT_TAG) {
|
|
2420
|
+
return (0, import_hwk_adapter_core4.failure)(
|
|
2421
|
+
import_hwk_adapter_core4.HardwareErrorCode.OperationTimeout,
|
|
2547
2422
|
e.message ?? "Keystone QR operation timed out"
|
|
2548
2423
|
);
|
|
2549
2424
|
}
|
|
2550
2425
|
}
|
|
2551
2426
|
const message = err instanceof Error ? err.message : String(err);
|
|
2552
2427
|
const params = err instanceof Error && err.stack ? { stack: err.stack } : void 0;
|
|
2553
|
-
return (0,
|
|
2428
|
+
return (0, import_hwk_adapter_core4.failure)(import_hwk_adapter_core4.HardwareErrorCode.UnknownError, message, params);
|
|
2554
2429
|
}
|
|
2555
2430
|
};
|
|
2556
2431
|
// Annotate the CommonJS export names for ESM import in node:
|