@onekeyfe/hd-shared 1.2.0-alpha.16 → 1.2.0-alpha.160
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/HardwareError.d.ts +6 -1
- package/dist/HardwareError.d.ts.map +1 -1
- package/dist/constants.d.ts +33 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/deviceType.d.ts +2 -1
- package/dist/deviceType.d.ts.map +1 -1
- package/dist/index.d.ts +325 -3
- package/dist/index.js +174 -2
- package/jest.config.js +5 -0
- package/package.json +3 -2
- package/src/HardwareError.test.ts +36 -0
- package/src/HardwareError.ts +37 -1
- package/src/constants.test.ts +194 -1
- package/src/constants.ts +218 -5
- package/src/deviceType.ts +1 -0
package/dist/index.js
CHANGED
|
@@ -135,6 +135,7 @@ const HardwareErrorCode = {
|
|
|
135
135
|
BleDeviceDisconnected: 720,
|
|
136
136
|
BlePoweredOff: 721,
|
|
137
137
|
BleUnsupported: 722,
|
|
138
|
+
BleUnavailableWhileUsbConnected: 723,
|
|
138
139
|
RuntimeError: 800,
|
|
139
140
|
PinInvalid: 801,
|
|
140
141
|
PinCancelled: 802,
|
|
@@ -164,7 +165,9 @@ const HardwareErrorCode = {
|
|
|
164
165
|
DeviceSettingsLanguageNotSupport: 826,
|
|
165
166
|
TooManyInputs: 827,
|
|
166
167
|
PinMismatch: 828,
|
|
167
|
-
DeviceLocked:
|
|
168
|
+
DeviceLocked: 830,
|
|
169
|
+
WalletSessionInvalid: 831,
|
|
170
|
+
NftStorageLimitReached: 832,
|
|
168
171
|
LowlevelTrasnportConnectError: 900,
|
|
169
172
|
WebDeviceNotFoundOrNeedsPermission: 901,
|
|
170
173
|
WebDevicePromptAccessError: 902,
|
|
@@ -240,6 +243,7 @@ const HardwareErrorCodeMessage = {
|
|
|
240
243
|
[HardwareErrorCode.BleDeviceDisconnected]: 'Device disconnected',
|
|
241
244
|
[HardwareErrorCode.BlePoweredOff]: 'Bluetooth is turned off',
|
|
242
245
|
[HardwareErrorCode.BleUnsupported]: 'Bluetooth is not supported on this device',
|
|
246
|
+
[HardwareErrorCode.BleUnavailableWhileUsbConnected]: 'Bluetooth is unavailable while USB is connected. Unplug USB and try again.',
|
|
243
247
|
[HardwareErrorCode.RuntimeError]: 'Runtime error',
|
|
244
248
|
[HardwareErrorCode.PinInvalid]: 'Pin invalid',
|
|
245
249
|
[HardwareErrorCode.PinCancelled]: 'Pin cancelled',
|
|
@@ -270,6 +274,8 @@ const HardwareErrorCodeMessage = {
|
|
|
270
274
|
[HardwareErrorCode.TooManyInputs]: 'Too many inputs',
|
|
271
275
|
[HardwareErrorCode.PinMismatch]: 'PIN mismatch',
|
|
272
276
|
[HardwareErrorCode.DeviceLocked]: 'Device locked',
|
|
277
|
+
[HardwareErrorCode.WalletSessionInvalid]: 'Wallet session is invalid or expired',
|
|
278
|
+
[HardwareErrorCode.NftStorageLimitReached]: 'NFT storage limit reached. Remove an NFT from the device and try again.',
|
|
273
279
|
[HardwareErrorCode.LowlevelTrasnportConnectError]: 'Lowlevel transport connect error',
|
|
274
280
|
[HardwareErrorCode.WebDeviceNotFoundOrNeedsPermission]: 'Web-USB or Web-Bluetooth device not found or needs permission',
|
|
275
281
|
[HardwareErrorCode.WebDevicePromptAccessError]: 'Web-USB or Web-Bluetooth device prompt access error',
|
|
@@ -280,6 +286,18 @@ const TypedError = (hardwareError, message, params) => {
|
|
|
280
286
|
}
|
|
281
287
|
return new HardwareError({ errorCode: hardwareError, message: message !== null && message !== void 0 ? message : '', params });
|
|
282
288
|
};
|
|
289
|
+
const isBleStaleBondHardwareError = (error) => {
|
|
290
|
+
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
291
|
+
return (code === HardwareErrorCode.BleDeviceBondError ||
|
|
292
|
+
code === HardwareErrorCode.BlePeerRemovedPairingInformation);
|
|
293
|
+
};
|
|
294
|
+
const isBleStaleBondErrorText = (text) => {
|
|
295
|
+
const value = text.toLowerCase();
|
|
296
|
+
return (value.includes('encryption is insufficient') ||
|
|
297
|
+
value.includes('insufficient encryption') ||
|
|
298
|
+
value.includes('peer removed pairing information') ||
|
|
299
|
+
value.includes('gatt_insuf_authentication'));
|
|
300
|
+
};
|
|
283
301
|
const serializeError = (payload) => {
|
|
284
302
|
if (payload && payload.error instanceof HardwareError) {
|
|
285
303
|
return {
|
|
@@ -371,6 +389,8 @@ var HardwareError$1 = /*#__PURE__*/Object.freeze({
|
|
|
371
389
|
HardwareErrorCode: HardwareErrorCode,
|
|
372
390
|
HardwareErrorCodeMessage: HardwareErrorCodeMessage,
|
|
373
391
|
TypedError: TypedError,
|
|
392
|
+
isBleStaleBondHardwareError: isBleStaleBondHardwareError,
|
|
393
|
+
isBleStaleBondErrorText: isBleStaleBondErrorText,
|
|
374
394
|
serializeError: serializeError,
|
|
375
395
|
CreateErrorByMessage: CreateErrorByMessage,
|
|
376
396
|
CreateHardwareErrorByBridgeError: CreateHardwareErrorByBridgeError,
|
|
@@ -393,6 +413,7 @@ const ONEKEY_WEBUSB_FILTER = [
|
|
|
393
413
|
{ vendorId: 0x1209, productId: 0x4f4b },
|
|
394
414
|
{ vendorId: 0x1209, productId: 0x4f4c },
|
|
395
415
|
];
|
|
416
|
+
const ONEKEY_PROTOCOL_V2_USB_IDS = [{ vendorId: 0x1209, productId: 0x4f4c }];
|
|
396
417
|
const TREZOR_WEBUSB_MANUFACTURER_NAMES = new Set(['trezor', 'trezor company', 'satoshilabs']);
|
|
397
418
|
const isKnownTrezorWebUsbDevice = (descriptor) => {
|
|
398
419
|
var _a;
|
|
@@ -434,13 +455,20 @@ exports.EOneKeyBleMessageKeys = void 0;
|
|
|
434
455
|
EOneKeyBleMessageKeys["NOBLE_BLE_STOP_SCAN"] = "$onekey-noble-ble-stop-scan";
|
|
435
456
|
EOneKeyBleMessageKeys["NOBLE_BLE_GET_DEVICE"] = "$onekey-noble-ble-get-device";
|
|
436
457
|
EOneKeyBleMessageKeys["NOBLE_BLE_CONNECT"] = "$onekey-noble-ble-connect";
|
|
458
|
+
EOneKeyBleMessageKeys["NOBLE_BLE_RELEASE"] = "$onekey-noble-ble-release";
|
|
437
459
|
EOneKeyBleMessageKeys["NOBLE_BLE_DISCONNECT"] = "$onekey-noble-ble-disconnect";
|
|
438
460
|
EOneKeyBleMessageKeys["NOBLE_BLE_WRITE"] = "$onekey-noble-ble-write";
|
|
439
461
|
EOneKeyBleMessageKeys["NOBLE_BLE_SUBSCRIBE"] = "$onekey-noble-ble-subscribe";
|
|
440
462
|
EOneKeyBleMessageKeys["NOBLE_BLE_UNSUBSCRIBE"] = "$onekey-noble-ble-unsubscribe";
|
|
441
463
|
EOneKeyBleMessageKeys["NOBLE_BLE_NOTIFICATION"] = "$onekey-noble-ble-notification";
|
|
464
|
+
EOneKeyBleMessageKeys["NOBLE_BLE_MTU_CHANGED"] = "$onekey-noble-ble-mtu-changed";
|
|
442
465
|
EOneKeyBleMessageKeys["NOBLE_BLE_CANCEL_PAIRING"] = "$onekey-noble-ble-cancel-pairing";
|
|
443
466
|
})(exports.EOneKeyBleMessageKeys || (exports.EOneKeyBleMessageKeys = {}));
|
|
467
|
+
exports.EBleDisconnectReason = void 0;
|
|
468
|
+
(function (EBleDisconnectReason) {
|
|
469
|
+
EBleDisconnectReason["DeviceDisconnected"] = "device-disconnected";
|
|
470
|
+
EBleDisconnectReason["IdleKeepAlive"] = "idle-keep-alive";
|
|
471
|
+
})(exports.EBleDisconnectReason || (exports.EBleDisconnectReason = {}));
|
|
444
472
|
const ONEKEY_SERVICE_UUID = '00000001-0000-1000-8000-00805f9b34fb';
|
|
445
473
|
const ONEKEY_WRITE_CHARACTERISTIC_UUID = '00000002-0000-1000-8000-00805f9b34fb';
|
|
446
474
|
const ONEKEY_NOTIFY_CHARACTERISTIC_UUID = '00000003-0000-1000-8000-00805f9b34fb';
|
|
@@ -487,11 +515,140 @@ const isOnekeyDevice = (name, id) => {
|
|
|
487
515
|
return true;
|
|
488
516
|
if (normalizedName.startsWith('touch ') ||
|
|
489
517
|
normalizedName.startsWith('pro ') ||
|
|
490
|
-
normalizedName.startsWith('pro2
|
|
518
|
+
normalizedName.startsWith('pro2') ||
|
|
519
|
+
normalizedName.startsWith('neo')) {
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
const compactName = compactBleName(normalizedName);
|
|
523
|
+
if (PRO2_COMPACT_NAME_PATTERN.test(compactName) || NEO_COMPACT_NAME_PATTERN.test(compactName)) {
|
|
491
524
|
return true;
|
|
492
525
|
}
|
|
493
526
|
return isOneKeyShortName(normalizedName);
|
|
494
527
|
};
|
|
528
|
+
const inferProtocolHintFromUsbId = (vendorId, productId) => ONEKEY_PROTOCOL_V2_USB_IDS.some(id => id.vendorId === vendorId && id.productId === productId)
|
|
529
|
+
? 'V2'
|
|
530
|
+
: undefined;
|
|
531
|
+
const resolveOneKeyUsbDevicePath = (device) => {
|
|
532
|
+
var _a, _b, _c, _d;
|
|
533
|
+
const serial = (_a = device.serialNumber) === null || _a === void 0 ? void 0 : _a.trim();
|
|
534
|
+
if (serial)
|
|
535
|
+
return serial;
|
|
536
|
+
if (device.vendorId == null && device.productId == null && !device.productName) {
|
|
537
|
+
return undefined;
|
|
538
|
+
}
|
|
539
|
+
const vendorId = ((_b = device.vendorId) !== null && _b !== void 0 ? _b : 0).toString(16).padStart(4, '0');
|
|
540
|
+
const productId = ((_c = device.productId) !== null && _c !== void 0 ? _c : 0).toString(16).padStart(4, '0');
|
|
541
|
+
const product = ((_d = device.productName) !== null && _d !== void 0 ? _d : 'onekey')
|
|
542
|
+
.trim()
|
|
543
|
+
.toLowerCase()
|
|
544
|
+
.replace(/\s+/g, '-')
|
|
545
|
+
.replace(/[^a-z0-9-]/g, '');
|
|
546
|
+
return `usb-${vendorId}-${productId}-${product || 'onekey'}`;
|
|
547
|
+
};
|
|
548
|
+
const BLUETOOTH_BASE_UUID_SUFFIX = '00001000800000805f9b34fb';
|
|
549
|
+
const normalizeBleUuid = (uuid) => (uuid !== null && uuid !== void 0 ? uuid : '').replace(/-/g, '').toLowerCase();
|
|
550
|
+
const createKnownBleUuidAliases = (uuid) => {
|
|
551
|
+
const normalized = normalizeBleUuid(uuid);
|
|
552
|
+
const aliases = new Set([normalized]);
|
|
553
|
+
if (normalized.length !== 32 || !normalized.endsWith(BLUETOOTH_BASE_UUID_SUFFIX)) {
|
|
554
|
+
return aliases;
|
|
555
|
+
}
|
|
556
|
+
const assignedNumber = normalized.slice(0, 8);
|
|
557
|
+
aliases.add(assignedNumber);
|
|
558
|
+
if (assignedNumber.startsWith('0000')) {
|
|
559
|
+
aliases.add(assignedNumber.slice(4));
|
|
560
|
+
}
|
|
561
|
+
return aliases;
|
|
562
|
+
};
|
|
563
|
+
const matchesKnownBleUuid = (actualUuid, aliases) => aliases.has(normalizeBleUuid(actualUuid));
|
|
564
|
+
const ONEKEY_COMMUNICATION_SERVICE_ALIASES = createKnownBleUuidAliases(ONEKEY_SERVICE_UUID);
|
|
565
|
+
const FIDO_SERVICE_ALIASES = createKnownBleUuidAliases('0000fffd-0000-1000-8000-00805f9b34fb');
|
|
566
|
+
const PRO2_COMPACT_NAME_PATTERN = /^(?:onekey)?pro2[a-f0-9]{4}$/i;
|
|
567
|
+
const NEO_COMPACT_NAME_PATTERN = /^(?:onekey)?neo[a-f0-9]{4}$/i;
|
|
568
|
+
const FIND_MY_COMPACT_SUFFIXES = [
|
|
569
|
+
'findemy',
|
|
570
|
+
'findem',
|
|
571
|
+
'finde',
|
|
572
|
+
'findmy',
|
|
573
|
+
'findm',
|
|
574
|
+
'find',
|
|
575
|
+
'fin',
|
|
576
|
+
];
|
|
577
|
+
const COMPLETE_FIND_MY_COMPACT_SUFFIXES = ['findemy', 'findmy'];
|
|
578
|
+
const compactBleName = (value) => Array.from(value)
|
|
579
|
+
.filter(character => character !== '-' && character.trim() !== '')
|
|
580
|
+
.join('')
|
|
581
|
+
.toLowerCase();
|
|
582
|
+
const getFindMyCompactSuffix = (compactName) => FIND_MY_COMPACT_SUFFIXES.find(candidate => compactName.endsWith(candidate));
|
|
583
|
+
const getPro2FindMyBaseLength = (value) => {
|
|
584
|
+
const compactName = compactBleName(value);
|
|
585
|
+
const suffix = getFindMyCompactSuffix(compactName);
|
|
586
|
+
if (!suffix)
|
|
587
|
+
return undefined;
|
|
588
|
+
const compactBaseName = compactName.slice(0, -suffix.length);
|
|
589
|
+
return PRO2_COMPACT_NAME_PATTERN.test(compactBaseName) ? compactBaseName.length : undefined;
|
|
590
|
+
};
|
|
591
|
+
const isPro2FindMyAdvertisementName = (value) => {
|
|
592
|
+
if (!value)
|
|
593
|
+
return false;
|
|
594
|
+
if (getPro2FindMyBaseLength(value) !== undefined)
|
|
595
|
+
return true;
|
|
596
|
+
const compactName = compactBleName(value);
|
|
597
|
+
const suffix = COMPLETE_FIND_MY_COMPACT_SUFFIXES.find(candidate => compactName.endsWith(candidate));
|
|
598
|
+
if (!suffix)
|
|
599
|
+
return false;
|
|
600
|
+
return compactName.slice(0, -suffix.length).includes('pro2');
|
|
601
|
+
};
|
|
602
|
+
const normalizePro2FindMyAdvertisementName = (value) => {
|
|
603
|
+
const baseLength = getPro2FindMyBaseLength(value);
|
|
604
|
+
if (baseLength === undefined)
|
|
605
|
+
return value;
|
|
606
|
+
let compactLength = 0;
|
|
607
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
608
|
+
const character = value[index];
|
|
609
|
+
if (character !== '-' && character.trim() !== '') {
|
|
610
|
+
compactLength += 1;
|
|
611
|
+
if (compactLength === baseLength) {
|
|
612
|
+
return value.slice(0, index + 1).trimEnd();
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return value;
|
|
617
|
+
};
|
|
618
|
+
const canonicalizePro2BleAdvertisementName = (value) => {
|
|
619
|
+
const withoutFindMy = normalizePro2FindMyAdvertisementName(value);
|
|
620
|
+
const compact = compactBleName(withoutFindMy);
|
|
621
|
+
if (!PRO2_COMPACT_NAME_PATTERN.test(compact))
|
|
622
|
+
return withoutFindMy;
|
|
623
|
+
const match = withoutFindMy.match(/^(onekey\s*)?pro\s*2\s*/i);
|
|
624
|
+
if (!match)
|
|
625
|
+
return withoutFindMy;
|
|
626
|
+
const rest = withoutFindMy.slice(match[0].length);
|
|
627
|
+
const prefix = match[1] ? 'OneKey Pro 2' : 'Pro 2';
|
|
628
|
+
return rest ? `${prefix} ${rest}` : prefix;
|
|
629
|
+
};
|
|
630
|
+
const isSameOnekeyBleName = (left, right) => {
|
|
631
|
+
if (!left || !right)
|
|
632
|
+
return false;
|
|
633
|
+
if (left === right)
|
|
634
|
+
return true;
|
|
635
|
+
return (compactBleName(canonicalizePro2BleAdvertisementName(left)) ===
|
|
636
|
+
compactBleName(canonicalizePro2BleAdvertisementName(right)));
|
|
637
|
+
};
|
|
638
|
+
const hasOnekeyCommunicationService = (serviceUuids) => (serviceUuids !== null && serviceUuids !== void 0 ? serviceUuids : []).some(uuid => matchesKnownBleUuid(uuid, ONEKEY_COMMUNICATION_SERVICE_ALIASES));
|
|
639
|
+
const isOnekeyBluetoothDevice = ({ id, name, localName, serviceUuids, }) => {
|
|
640
|
+
const advertisedServiceUuids = serviceUuids !== null && serviceUuids !== void 0 ? serviceUuids : [];
|
|
641
|
+
if (hasOnekeyCommunicationService(advertisedServiceUuids)) {
|
|
642
|
+
return true;
|
|
643
|
+
}
|
|
644
|
+
if (isPro2FindMyAdvertisementName(name) || isPro2FindMyAdvertisementName(localName)) {
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
if (advertisedServiceUuids.some(uuid => matchesKnownBleUuid(uuid, FIDO_SERVICE_ALIASES))) {
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
return isOnekeyDevice(name !== null && name !== void 0 ? name : null, id) || isOnekeyDevice(localName !== null && localName !== void 0 ? localName : null, id);
|
|
651
|
+
};
|
|
495
652
|
|
|
496
653
|
/******************************************************************************
|
|
497
654
|
Copyright (c) Microsoft Corporation.
|
|
@@ -564,6 +721,7 @@ exports.EDeviceType = void 0;
|
|
|
564
721
|
EDeviceType["Touch"] = "touch";
|
|
565
722
|
EDeviceType["Pro"] = "pro";
|
|
566
723
|
EDeviceType["Pro2"] = "pro2";
|
|
724
|
+
EDeviceType["Neo"] = "neo";
|
|
567
725
|
})(exports.EDeviceType || (exports.EDeviceType = {}));
|
|
568
726
|
|
|
569
727
|
exports.CreateErrorByMessage = CreateErrorByMessage;
|
|
@@ -576,19 +734,33 @@ exports.HardwareError = HardwareError;
|
|
|
576
734
|
exports.HardwareErrorCode = HardwareErrorCode;
|
|
577
735
|
exports.HardwareErrorCodeMessage = HardwareErrorCodeMessage;
|
|
578
736
|
exports.ONEKEY_NOTIFY_CHARACTERISTIC_UUID = ONEKEY_NOTIFY_CHARACTERISTIC_UUID;
|
|
737
|
+
exports.ONEKEY_PROTOCOL_V2_USB_IDS = ONEKEY_PROTOCOL_V2_USB_IDS;
|
|
579
738
|
exports.ONEKEY_SERVICE_UUID = ONEKEY_SERVICE_UUID;
|
|
580
739
|
exports.ONEKEY_WEBUSB_FILTER = ONEKEY_WEBUSB_FILTER;
|
|
581
740
|
exports.ONEKEY_WRITE_CHARACTERISTIC_UUID = ONEKEY_WRITE_CHARACTERISTIC_UUID;
|
|
582
741
|
exports.TypedError = TypedError;
|
|
742
|
+
exports.canonicalizePro2BleAdvertisementName = canonicalizePro2BleAdvertisementName;
|
|
583
743
|
exports.createDefectiveFirmwareError = createDefectiveFirmwareError;
|
|
584
744
|
exports.createDeferred = createDeferred;
|
|
585
745
|
exports.createDeprecatedHardwareError = createDeprecatedHardwareError;
|
|
586
746
|
exports.createDeviceNotSupportMethodError = createDeviceNotSupportMethodError;
|
|
747
|
+
exports.createKnownBleUuidAliases = createKnownBleUuidAliases;
|
|
587
748
|
exports.createNeedUpgradeFirmwareHardwareError = createNeedUpgradeFirmwareHardwareError;
|
|
588
749
|
exports.createNewFirmwareForceUpdateHardwareError = createNewFirmwareForceUpdateHardwareError;
|
|
589
750
|
exports.createNewFirmwareUnReleaseHardwareError = createNewFirmwareUnReleaseHardwareError;
|
|
751
|
+
exports.hasOnekeyCommunicationService = hasOnekeyCommunicationService;
|
|
752
|
+
exports.inferProtocolHintFromUsbId = inferProtocolHintFromUsbId;
|
|
753
|
+
exports.isBleStaleBondErrorText = isBleStaleBondErrorText;
|
|
754
|
+
exports.isBleStaleBondHardwareError = isBleStaleBondHardwareError;
|
|
590
755
|
exports.isHeaderChunk = isHeaderChunk;
|
|
591
756
|
exports.isKnownTrezorWebUsbDevice = isKnownTrezorWebUsbDevice;
|
|
757
|
+
exports.isOnekeyBluetoothDevice = isOnekeyBluetoothDevice;
|
|
592
758
|
exports.isOnekeyDevice = isOnekeyDevice;
|
|
759
|
+
exports.isPro2FindMyAdvertisementName = isPro2FindMyAdvertisementName;
|
|
760
|
+
exports.isSameOnekeyBleName = isSameOnekeyBleName;
|
|
761
|
+
exports.matchesKnownBleUuid = matchesKnownBleUuid;
|
|
762
|
+
exports.normalizeBleUuid = normalizeBleUuid;
|
|
763
|
+
exports.normalizePro2FindMyAdvertisementName = normalizePro2FindMyAdvertisementName;
|
|
764
|
+
exports.resolveOneKeyUsbDevicePath = resolveOneKeyUsbDevicePath;
|
|
593
765
|
exports.serializeError = serializeError;
|
|
594
766
|
exports.wait = wait;
|
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-shared",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.160",
|
|
4
4
|
"description": "Hardware SDK's shared tool library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Hardware-SDK",
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w",
|
|
24
24
|
"build": "rimraf dist && rollup -c ../../build/rollup.config.js",
|
|
25
|
+
"test": "jest",
|
|
25
26
|
"lint": "eslint .",
|
|
26
27
|
"lint:fix": "eslint . --fix"
|
|
27
28
|
},
|
|
28
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "df1736352e44ac76af4185eb7a6112a081b0d68c"
|
|
29
30
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HardwareErrorCode,
|
|
3
|
+
HardwareErrorCodeMessage,
|
|
4
|
+
TypedError,
|
|
5
|
+
serializeError,
|
|
6
|
+
} from './HardwareError';
|
|
7
|
+
|
|
8
|
+
describe('HardwareErrorCode compatibility', () => {
|
|
9
|
+
test('preserves published Protocol V2 values while leaving code 829 unused', () => {
|
|
10
|
+
expect(HardwareErrorCode).not.toHaveProperty('KaspaPrevTxIdMismatch');
|
|
11
|
+
expect(HardwareErrorCode.DeviceLocked).toBe(830);
|
|
12
|
+
expect(HardwareErrorCode.WalletSessionInvalid).toBe(831);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('reserves the BLE and USB conflict code for app error mapping', () => {
|
|
16
|
+
expect(HardwareErrorCode.BleUnavailableWhileUsbConnected).toBe(723);
|
|
17
|
+
expect(HardwareErrorCodeMessage[HardwareErrorCode.BleUnavailableWhileUsbConnected]).toBe(
|
|
18
|
+
'Bluetooth is unavailable while USB is connected. Unplug USB and try again.'
|
|
19
|
+
);
|
|
20
|
+
expect(
|
|
21
|
+
serializeError({
|
|
22
|
+
error: TypedError(HardwareErrorCode.BleUnavailableWhileUsbConnected, undefined, {
|
|
23
|
+
failureCode: 'Failure_ProcessError',
|
|
24
|
+
firmwareMessage: 'link disabled',
|
|
25
|
+
}),
|
|
26
|
+
})
|
|
27
|
+
).toEqual({
|
|
28
|
+
code: 723,
|
|
29
|
+
error: 'Bluetooth is unavailable while USB is connected. Unplug USB and try again.',
|
|
30
|
+
params: {
|
|
31
|
+
failureCode: 'Failure_ProcessError',
|
|
32
|
+
firmwareMessage: 'link disabled',
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
package/src/HardwareError.ts
CHANGED
|
@@ -345,6 +345,7 @@ export const HardwareErrorCode = {
|
|
|
345
345
|
BleDeviceDisconnected: 720,
|
|
346
346
|
BlePoweredOff: 721,
|
|
347
347
|
BleUnsupported: 722,
|
|
348
|
+
BleUnavailableWhileUsbConnected: 723,
|
|
348
349
|
|
|
349
350
|
/**
|
|
350
351
|
* Hardware runtiome errors
|
|
@@ -488,7 +489,19 @@ export const HardwareErrorCode = {
|
|
|
488
489
|
* Protocol V2 device must be unlocked before the requested operation.
|
|
489
490
|
* @params: { failureCode?: string; subcode?: number; firmwareMessage?: string }
|
|
490
491
|
*/
|
|
491
|
-
DeviceLocked:
|
|
492
|
+
DeviceLocked: 830,
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* The cached wallet session is invalid or has expired.
|
|
496
|
+
* @params: { failureCode?: string; subcode?: number; firmwareMessage?: string }
|
|
497
|
+
*/
|
|
498
|
+
WalletSessionInvalid: 831,
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Pro2 NFT storage has reached its item limit.
|
|
502
|
+
* @params: { count: number; limit: number }
|
|
503
|
+
*/
|
|
504
|
+
NftStorageLimitReached: 832,
|
|
492
505
|
|
|
493
506
|
/**
|
|
494
507
|
* Lowlevel transport connect error
|
|
@@ -607,6 +620,8 @@ export const HardwareErrorCodeMessage: HardwareErrorCodeMessageMapping = {
|
|
|
607
620
|
[HardwareErrorCode.BleDeviceDisconnected]: 'Device disconnected',
|
|
608
621
|
[HardwareErrorCode.BlePoweredOff]: 'Bluetooth is turned off',
|
|
609
622
|
[HardwareErrorCode.BleUnsupported]: 'Bluetooth is not supported on this device',
|
|
623
|
+
[HardwareErrorCode.BleUnavailableWhileUsbConnected]:
|
|
624
|
+
'Bluetooth is unavailable while USB is connected. Unplug USB and try again.',
|
|
610
625
|
|
|
611
626
|
/**
|
|
612
627
|
* Runtime Error
|
|
@@ -642,6 +657,9 @@ export const HardwareErrorCodeMessage: HardwareErrorCodeMessageMapping = {
|
|
|
642
657
|
[HardwareErrorCode.TooManyInputs]: 'Too many inputs',
|
|
643
658
|
[HardwareErrorCode.PinMismatch]: 'PIN mismatch',
|
|
644
659
|
[HardwareErrorCode.DeviceLocked]: 'Device locked',
|
|
660
|
+
[HardwareErrorCode.WalletSessionInvalid]: 'Wallet session is invalid or expired',
|
|
661
|
+
[HardwareErrorCode.NftStorageLimitReached]:
|
|
662
|
+
'NFT storage limit reached. Remove an NFT from the device and try again.',
|
|
645
663
|
|
|
646
664
|
/**
|
|
647
665
|
* Lowlevel transport
|
|
@@ -664,6 +682,24 @@ export const TypedError = (
|
|
|
664
682
|
return new HardwareError({ errorCode: hardwareError, message: message ?? '', params });
|
|
665
683
|
};
|
|
666
684
|
|
|
685
|
+
export const isBleStaleBondHardwareError = (error: unknown): boolean => {
|
|
686
|
+
const code = (error as { errorCode?: unknown })?.errorCode;
|
|
687
|
+
return (
|
|
688
|
+
code === HardwareErrorCode.BleDeviceBondError ||
|
|
689
|
+
code === HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
690
|
+
);
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
export const isBleStaleBondErrorText = (text: string): boolean => {
|
|
694
|
+
const value = text.toLowerCase();
|
|
695
|
+
return (
|
|
696
|
+
value.includes('encryption is insufficient') ||
|
|
697
|
+
value.includes('insufficient encryption') ||
|
|
698
|
+
value.includes('peer removed pairing information') ||
|
|
699
|
+
value.includes('gatt_insuf_authentication')
|
|
700
|
+
);
|
|
701
|
+
};
|
|
702
|
+
|
|
667
703
|
export const serializeError = (payload: any) => {
|
|
668
704
|
if (payload && payload.error instanceof HardwareError) {
|
|
669
705
|
return {
|
package/src/constants.test.ts
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
canonicalizePro2BleAdvertisementName,
|
|
3
|
+
createKnownBleUuidAliases,
|
|
4
|
+
hasOnekeyCommunicationService,
|
|
5
|
+
inferProtocolHintFromUsbId,
|
|
6
|
+
isKnownTrezorWebUsbDevice,
|
|
7
|
+
isOnekeyBluetoothDevice,
|
|
8
|
+
isOnekeyDevice,
|
|
9
|
+
isPro2FindMyAdvertisementName,
|
|
10
|
+
isSameOnekeyBleName,
|
|
11
|
+
matchesKnownBleUuid,
|
|
12
|
+
normalizePro2FindMyAdvertisementName,
|
|
13
|
+
resolveOneKeyUsbDevicePath,
|
|
14
|
+
} from './constants';
|
|
2
15
|
|
|
3
16
|
describe('hardware device identity filters', () => {
|
|
4
17
|
it('accepts known OneKey BLE names', () => {
|
|
5
18
|
expect(isOnekeyDevice('Touch A1B2')).toBe(true);
|
|
6
19
|
expect(isOnekeyDevice('Pro A1B2')).toBe(true);
|
|
20
|
+
expect(isOnekeyDevice('Neo A1B2')).toBe(true);
|
|
21
|
+
expect(isOnekeyDevice('Pro2A1B2')).toBe(true);
|
|
22
|
+
expect(isOnekeyDevice('Pro 2 A1B2')).toBe(true);
|
|
23
|
+
expect(isOnekeyDevice('OneKeyPro2A1B2')).toBe(true);
|
|
24
|
+
expect(isOnekeyDevice('OneKey Pro 2 A1B2')).toBe(true);
|
|
25
|
+
expect(isOnekeyDevice('Neo22D8')).toBe(true);
|
|
7
26
|
expect(isOnekeyDevice('K1234')).toBe(true);
|
|
8
27
|
expect(isOnekeyDevice('S8')).toBe(true);
|
|
9
28
|
});
|
|
@@ -13,6 +32,180 @@ describe('hardware device identity filters', () => {
|
|
|
13
32
|
expect(isOnekeyDevice('Ledger Nano X')).toBe(false);
|
|
14
33
|
});
|
|
15
34
|
|
|
35
|
+
it('does not identify an FFFD-only advertisement as OneKey', () => {
|
|
36
|
+
expect(isOnekeyBluetoothDevice({ name: 'Find My', serviceUuids: ['fffd'] })).toBe(false);
|
|
37
|
+
expect(
|
|
38
|
+
isOnekeyBluetoothDevice({ serviceUuids: ['0000fffd-0000-1000-8000-00805f9b34fb'] })
|
|
39
|
+
).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('identifies Pro2 communication advertisements independently of their display name', () => {
|
|
43
|
+
expect(
|
|
44
|
+
isOnekeyBluetoothDevice({
|
|
45
|
+
name: 'Pro2 A1B2',
|
|
46
|
+
serviceUuids: ['0001', 'fffd'],
|
|
47
|
+
})
|
|
48
|
+
).toBe(true);
|
|
49
|
+
expect(
|
|
50
|
+
isOnekeyBluetoothDevice({
|
|
51
|
+
name: 'Pro 2 A1B2',
|
|
52
|
+
serviceUuids: ['0001', 'fffd'],
|
|
53
|
+
})
|
|
54
|
+
).toBe(true);
|
|
55
|
+
expect(
|
|
56
|
+
isOnekeyBluetoothDevice({
|
|
57
|
+
localName: 'Pro2 A1B2 - Find My',
|
|
58
|
+
serviceUuids: ['0001', 'fffd'],
|
|
59
|
+
})
|
|
60
|
+
).toBe(true);
|
|
61
|
+
expect(
|
|
62
|
+
isOnekeyBluetoothDevice({
|
|
63
|
+
name: 'Pro2 5E9D - Finde My',
|
|
64
|
+
serviceUuids: ['0001', 'fffd'],
|
|
65
|
+
})
|
|
66
|
+
).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('rejects Pro2 Find My entries when the communication service is absent', () => {
|
|
70
|
+
expect(
|
|
71
|
+
isOnekeyBluetoothDevice({
|
|
72
|
+
name: 'Pro2 A1B2 - Find My',
|
|
73
|
+
})
|
|
74
|
+
).toBe(false);
|
|
75
|
+
expect(
|
|
76
|
+
isOnekeyBluetoothDevice({
|
|
77
|
+
localName: 'OneKey Pro 2 A1B2 - Find My',
|
|
78
|
+
serviceUuids: [],
|
|
79
|
+
})
|
|
80
|
+
).toBe(false);
|
|
81
|
+
expect(
|
|
82
|
+
isOnekeyBluetoothDevice({
|
|
83
|
+
name: 'Pro2 5E9D - Finde My',
|
|
84
|
+
serviceUuids: ['fffd'],
|
|
85
|
+
})
|
|
86
|
+
).toBe(false);
|
|
87
|
+
expect(isOnekeyBluetoothDevice({ name: 'Pro2 - Find My' })).toBe(false);
|
|
88
|
+
expect(isOnekeyBluetoothDevice({ name: 'Pro2 G12Z - Find My' })).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('uses a broad Find My guard without broadening public name normalization', () => {
|
|
92
|
+
expect(isPro2FindMyAdvertisementName('Pro2 Griffin - Find My')).toBe(true);
|
|
93
|
+
expect(isPro2FindMyAdvertisementName('Pro2 Griffin')).toBe(false);
|
|
94
|
+
expect(isPro2FindMyAdvertisementName('Pro2 22D8 - Find My')).toBe(true);
|
|
95
|
+
expect(normalizePro2FindMyAdvertisementName('Pro2 Griffin - Find My')).toBe(
|
|
96
|
+
'Pro2 Griffin - Find My'
|
|
97
|
+
);
|
|
98
|
+
expect(normalizePro2FindMyAdvertisementName('Pro2 22D8 - Find My')).toBe('Pro2 22D8');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it.each([
|
|
102
|
+
['Pro2 A1B2 - Find My', 'Pro2 A1B2'],
|
|
103
|
+
['Pro2 5E9D - Finde My', 'Pro2 5E9D'],
|
|
104
|
+
[' OneKey Pro 2 A1B2 Find My ', ' OneKey Pro 2 A1B2'],
|
|
105
|
+
['OneKey Pro-2 22D8--Find-My', 'OneKey Pro-2 22D8'],
|
|
106
|
+
['Pro2 22D8FindMy', 'Pro2 22D8'],
|
|
107
|
+
['Pro2 22D8 - Fin', 'Pro2 22D8'],
|
|
108
|
+
['Pro2 22D8FindM', 'Pro2 22D8'],
|
|
109
|
+
[`Pro2 22D8${'\t'.repeat(10_000)}Find My`, 'Pro2 22D8'],
|
|
110
|
+
['Pro2 A1B2', 'Pro2 A1B2'],
|
|
111
|
+
['Pro2 Griffin', 'Pro2 Griffin'],
|
|
112
|
+
['Find My', 'Find My'],
|
|
113
|
+
])('normalizes the public Pro2 advertisement name %s', (name, expected) => {
|
|
114
|
+
expect(normalizePro2FindMyAdvertisementName(name)).toBe(expected);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it.each([
|
|
118
|
+
['Pro2 A1B2', 'Pro 2 A1B2'],
|
|
119
|
+
['Pro2A1B2', 'Pro 2 A1B2'],
|
|
120
|
+
['Pro 2 A1B2', 'Pro 2 A1B2'],
|
|
121
|
+
['Pro 2 0088', 'Pro 2 0088'],
|
|
122
|
+
['Pro2 22D8 - Find My', 'Pro 2 22D8'],
|
|
123
|
+
['Pro 2 A1B2 - Find My', 'Pro 2 A1B2'],
|
|
124
|
+
['OneKeyPro2A1B2', 'OneKey Pro 2 A1B2'],
|
|
125
|
+
['OneKey Pro 2 A1B2', 'OneKey Pro 2 A1B2'],
|
|
126
|
+
['OneKey Pro 2', 'OneKey Pro 2'],
|
|
127
|
+
['Pro A1B2', 'Pro A1B2'],
|
|
128
|
+
['Pro 22D8', 'Pro 22D8'],
|
|
129
|
+
['Pro 2D8F', 'Pro 2D8F'],
|
|
130
|
+
['OneKey Pro 22D8', 'OneKey Pro 22D8'],
|
|
131
|
+
['Neo 22D8', 'Neo 22D8'],
|
|
132
|
+
])('canonicalizes the public Pro2 advertisement name %s', (name, expected) => {
|
|
133
|
+
expect(canonicalizePro2BleAdvertisementName(name)).toBe(expected);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('treats compact and spaced Pro2 BLE names as the same device', () => {
|
|
137
|
+
expect(isSameOnekeyBleName('Pro2 6136', 'Pro 2 6136')).toBe(true);
|
|
138
|
+
expect(isSameOnekeyBleName('Pro2 6136 - Find My', 'Pro 2 6136')).toBe(true);
|
|
139
|
+
expect(isSameOnekeyBleName('Pro 2 6136', 'Pro 2 0088')).toBe(false);
|
|
140
|
+
expect(isSameOnekeyBleName('Pro A1B2', 'Pro 2 A1B2')).toBe(false);
|
|
141
|
+
expect(isSameOnekeyBleName('Pro 22D8', 'Pro 22D8')).toBe(true);
|
|
142
|
+
expect(isSameOnekeyBleName('Pro 22D8', 'Pro2 22D8')).toBe(false);
|
|
143
|
+
expect(isSameOnekeyBleName('Pro 22D8', 'Pro 2 22D8')).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('keeps OneKey discovery on the communication service', () => {
|
|
147
|
+
expect(
|
|
148
|
+
isOnekeyBluetoothDevice({
|
|
149
|
+
name: 'OneKey Pro 2',
|
|
150
|
+
serviceUuids: ['00000001-0000-1000-8000-00805f9b34fb'],
|
|
151
|
+
})
|
|
152
|
+
).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('only aliases standard Bluetooth Base UUID representations', () => {
|
|
156
|
+
const aliases = createKnownBleUuidAliases('00000001-0000-1000-8000-00805f9b34fb');
|
|
157
|
+
|
|
158
|
+
expect(matchesKnownBleUuid('0001', aliases)).toBe(true);
|
|
159
|
+
expect(matchesKnownBleUuid('00000001', aliases)).toBe(true);
|
|
160
|
+
expect(matchesKnownBleUuid('abcd0001-1234-5678-9012-abcdefabcdef', aliases)).toBe(false);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('does not treat vendor-specific UUID fragments as communication or FIDO services', () => {
|
|
164
|
+
expect(
|
|
165
|
+
isOnekeyBluetoothDevice({
|
|
166
|
+
name: 'OneKey Pro 2',
|
|
167
|
+
serviceUuids: ['abcdfffd-1234-5678-9012-abcdefabcdef'],
|
|
168
|
+
})
|
|
169
|
+
).toBe(true);
|
|
170
|
+
expect(
|
|
171
|
+
isOnekeyBluetoothDevice({
|
|
172
|
+
serviceUuids: ['abcd0001-1234-5678-9012-abcdefabcdef'],
|
|
173
|
+
})
|
|
174
|
+
).toBe(false);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('requires an explicitly advertised communication service for strict discovery', () => {
|
|
178
|
+
expect(hasOnekeyCommunicationService([])).toBe(false);
|
|
179
|
+
expect(hasOnekeyCommunicationService(['0001'])).toBe(true);
|
|
180
|
+
expect(hasOnekeyCommunicationService(['abcd0001-1234-5678-9012-abcdefabcdef'])).toBe(false);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('hints Protocol V2 from the current Pro2/Neo USB ID only', () => {
|
|
184
|
+
expect(inferProtocolHintFromUsbId(0x1209, 0x4f4c)).toBe('V2');
|
|
185
|
+
expect(inferProtocolHintFromUsbId(0x1209, 0x53c1)).toBeUndefined();
|
|
186
|
+
expect(inferProtocolHintFromUsbId(0x1209, 0x4f4a)).toBeUndefined();
|
|
187
|
+
expect(inferProtocolHintFromUsbId(0x1209, 0x4f4b)).toBeUndefined();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('uses the USB serial when present and synthesizes a path when firmware omits it', () => {
|
|
191
|
+
expect(
|
|
192
|
+
resolveOneKeyUsbDevicePath({
|
|
193
|
+
vendorId: 0x1209,
|
|
194
|
+
productId: 0x4f4c,
|
|
195
|
+
productName: 'OneKey Pro 2',
|
|
196
|
+
serialNumber: 'PRO2-SERIAL',
|
|
197
|
+
})
|
|
198
|
+
).toBe('PRO2-SERIAL');
|
|
199
|
+
expect(
|
|
200
|
+
resolveOneKeyUsbDevicePath({
|
|
201
|
+
vendorId: 0x1209,
|
|
202
|
+
productId: 0x4f4c,
|
|
203
|
+
productName: 'OneKey Neo',
|
|
204
|
+
serialNumber: '',
|
|
205
|
+
})
|
|
206
|
+
).toBe('usb-1209-4f4c-onekey-neo');
|
|
207
|
+
});
|
|
208
|
+
|
|
16
209
|
it('only filters WebUSB descriptors that are explicitly identified as Trezor', () => {
|
|
17
210
|
expect(
|
|
18
211
|
isKnownTrezorWebUsbDevice({
|