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