@onekeyfe/hd-core 1.2.0-alpha.1 → 1.2.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/connectSettings.test.ts +19 -0
- package/__tests__/device-wallet-session-store.test.ts +131 -0
- package/__tests__/preInitialize.test.ts +33 -0
- package/__tests__/protocol-v2-firmware-targets.test.ts +19 -0
- package/__tests__/protocol-v2.test.ts +1784 -502
- package/dist/api/ClearSessionCache.d.ts +9 -0
- package/dist/api/ClearSessionCache.d.ts.map +1 -0
- package/dist/api/FileRead.d.ts.map +1 -1
- package/dist/api/FileWrite.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +24 -3
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +1 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +4 -0
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/progressThrottle.d.ts +3 -0
- package/dist/api/firmware/progressThrottle.d.ts.map +1 -0
- package/dist/api/index.d.ts +8 -10
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts +6 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts +7 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts +3 -2
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/{DeviceGetDeviceInfo.d.ts → DeviceInfoGet.d.ts} +10 -10
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceSessionGet.d.ts +9 -0
- package/dist/api/protocol-v2/DeviceSessionGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceStatusGet.d.ts +6 -0
- package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/FilesystemFormat.d.ts.map +1 -1
- package/dist/api/protocol-v2/{FilesystemFixPermission.d.ts → FilesystemPermissionFix.d.ts} +2 -2
- package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts.map +1 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts +6 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -0
- package/dist/api/protocol-v2/helpers.d.ts +27 -18
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/api/ton/TonSignData.d.ts +1 -5
- package/dist/api/ton/TonSignData.d.ts.map +1 -1
- package/dist/api/tron/TronSignMessage.d.ts +0 -1
- package/dist/api/tron/TronSignMessage.d.ts.map +1 -1
- package/dist/api/tron/TronSignTransaction.d.ts +0 -1
- package/dist/api/tron/TronSignTransaction.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +1 -1
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts +6 -3
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceWalletSessionStore.d.ts +15 -0
- package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -0
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
- package/dist/index.d.ts +96 -55
- package/dist/index.js +2077 -1609
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/features.d.ts +12 -14
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/firmware.d.ts +11 -9
- package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +1 -0
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +13 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdate.d.ts +4 -6
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/getDeviceInfo.d.ts +2 -0
- package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +11 -11
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +16 -17
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/api/sessionCache.d.ts +10 -0
- package/dist/types/api/sessionCache.d.ts.map +1 -0
- package/dist/types/device.d.ts +1 -0
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/params.d.ts +1 -0
- package/dist/types/params.d.ts.map +1 -1
- package/dist/types/settings.d.ts +21 -2
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +2 -0
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/ClearSessionCache.ts +28 -0
- package/src/api/FileRead.ts +16 -3
- package/src/api/FileWrite.ts +14 -1
- package/src/api/FirmwareUpdateV4.ts +1075 -264
- package/src/api/GetOnekeyFeatures.ts +1 -1
- package/src/api/GetPassphraseState.ts +4 -1
- package/src/api/device/DeviceRebootToBoardloader.ts +4 -4
- package/src/api/device/DeviceRebootToBootloader.ts +4 -4
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +18 -4
- package/src/api/firmware/progressThrottle.ts +44 -0
- package/src/api/index.ts +8 -10
- package/src/api/protocol-v2/{FactoryGetDeviceInfo.ts → DeviceFactoryInfoGet.ts} +3 -3
- package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +32 -0
- package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +3 -5
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +39 -5
- package/src/api/protocol-v2/{DeviceGetDeviceInfo.ts → DeviceInfoGet.ts} +52 -22
- package/src/api/protocol-v2/DeviceReboot.ts +1 -1
- package/src/api/protocol-v2/DeviceSessionGet.ts +26 -0
- package/src/api/protocol-v2/DeviceStatusGet.ts +14 -0
- package/src/api/protocol-v2/FilesystemFormat.ts +4 -1
- package/src/api/protocol-v2/{FilesystemFixPermission.ts → FilesystemPermissionFix.ts} +2 -2
- package/src/api/protocol-v2/{GetProtoVersion.ts → ProtocolInfoRequest.ts} +2 -2
- package/src/api/protocol-v2/helpers.ts +71 -47
- package/src/api/ton/TonSignData.ts +1 -5
- package/src/api/tron/TronSignMessage.ts +0 -1
- package/src/api/tron/TronSignTransaction.ts +0 -1
- package/src/core/index.ts +19 -5
- package/src/data/messages/messages-protocol-v2.json +599 -1033
- package/src/data/messages/messages.json +8 -0
- package/src/data-manager/DataManager.ts +7 -0
- package/src/data-manager/connectSettings.ts +2 -4
- package/src/device/Device.ts +89 -70
- package/src/device/DeviceWalletSessionStore.ts +78 -0
- package/src/deviceProfile/buildDeviceFeatures.ts +60 -63
- package/src/deviceProfile/buildDeviceProfile.ts +77 -58
- package/src/inject.ts +13 -16
- package/src/protocols/protocol-v2/features.ts +26 -63
- package/src/protocols/protocol-v2/firmware.ts +12 -36
- package/src/protocols/protocol-v2/index.ts +1 -0
- package/src/protocols/protocol-v2/walletSession.ts +83 -0
- package/src/types/api/firmwareUpdate.ts +16 -20
- package/src/types/api/getDeviceInfo.ts +2 -0
- package/src/types/api/index.ts +17 -20
- package/src/types/api/protocolV2.ts +36 -40
- package/src/types/api/sessionCache.ts +14 -0
- package/src/types/device.ts +1 -0
- package/src/types/params.ts +4 -0
- package/src/types/settings.ts +42 -35
- package/src/utils/deviceFeaturesUtils.ts +44 -13
- package/dist/api/protocol-v2/DevReboot.d.ts +0 -7
- package/dist/api/protocol-v2/DevReboot.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceGetDeviceInfo.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +0 -6
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +0 -1
- package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts +0 -7
- package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts.map +0 -1
- package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts +0 -6
- package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts.map +0 -1
- package/dist/api/protocol-v2/FilesystemFixPermission.d.ts.map +0 -1
- package/dist/api/protocol-v2/GetProtoVersion.d.ts +0 -6
- package/dist/api/protocol-v2/GetProtoVersion.d.ts.map +0 -1
- package/src/api/protocol-v2/DevReboot.ts +0 -24
- package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +0 -16
- package/src/api/protocol-v2/FactoryDeviceInfoSettings.ts +0 -29
|
@@ -34,6 +34,7 @@ const Log = getLogger(LoggerNames.Core);
|
|
|
34
34
|
|
|
35
35
|
export const FIRMWARE_FIELDS = [
|
|
36
36
|
'firmware',
|
|
37
|
+
'firmware-v1',
|
|
37
38
|
'firmware-v2',
|
|
38
39
|
'firmware-v8',
|
|
39
40
|
'firmware-btc-v8',
|
|
@@ -46,6 +47,7 @@ export type ProtobufMessageSchema = ProtocolV1MessageSchema | 'v2Schema';
|
|
|
46
47
|
|
|
47
48
|
const FIRMWARE_FIELD_TYPE_MAP: Readonly<Record<IFirmwareField, EFirmwareType>> = {
|
|
48
49
|
firmware: EFirmwareType.Universal,
|
|
50
|
+
'firmware-v1': EFirmwareType.Universal,
|
|
49
51
|
'firmware-v2': EFirmwareType.Universal,
|
|
50
52
|
'firmware-v8': EFirmwareType.Universal,
|
|
51
53
|
'firmware-btc-v8': EFirmwareType.BitcoinOnly,
|
|
@@ -87,6 +89,10 @@ export default class DataManager {
|
|
|
87
89
|
firmware: [],
|
|
88
90
|
ble: [],
|
|
89
91
|
},
|
|
92
|
+
[EDeviceType.Pro2]: {
|
|
93
|
+
firmware: [],
|
|
94
|
+
ble: [],
|
|
95
|
+
},
|
|
90
96
|
[EDeviceType.ClassicPure]: {
|
|
91
97
|
firmware: [],
|
|
92
98
|
ble: [],
|
|
@@ -447,6 +453,7 @@ export default class DataManager {
|
|
|
447
453
|
[EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
|
|
448
454
|
[EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
|
|
449
455
|
[EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
|
|
456
|
+
[EDeviceType.Pro2]: this.enrichFirmwareReleaseInfo(data.pro2),
|
|
450
457
|
};
|
|
451
458
|
this.assets = {
|
|
452
459
|
bridge: data.bridge,
|
|
@@ -27,8 +27,6 @@ const initialSettings: ConnectSettings = {
|
|
|
27
27
|
env: 'web',
|
|
28
28
|
lazyLoad: false,
|
|
29
29
|
timestamp: new Date().getTime(),
|
|
30
|
-
// 临时开关:仅用于本地/测试固件兼容;正式链路默认调用真实 DevGetDeviceInfo。
|
|
31
|
-
protocolV2DeviceInfoMockEnabled: false,
|
|
32
30
|
};
|
|
33
31
|
|
|
34
32
|
export const getEnv = () => {
|
|
@@ -117,8 +115,8 @@ export const parseConnectSettings = (input: Partial<ConnectSettings> = {}) => {
|
|
|
117
115
|
settings.fetchConfig = input.fetchConfig;
|
|
118
116
|
}
|
|
119
117
|
|
|
120
|
-
if (
|
|
121
|
-
settings.
|
|
118
|
+
if (input.configFetcher) {
|
|
119
|
+
settings.configFetcher = input.configFetcher;
|
|
122
120
|
}
|
|
123
121
|
|
|
124
122
|
return settings;
|
package/src/device/Device.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
HardwareError,
|
|
10
10
|
HardwareErrorCode,
|
|
11
11
|
createDeferred,
|
|
12
|
+
createDeviceNotSupportMethodError,
|
|
12
13
|
} from '@onekeyfe/hd-shared';
|
|
13
14
|
|
|
14
15
|
import {
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
import { generateInstanceId } from '../utils/tracing';
|
|
32
33
|
// eslint-disable-next-line import/no-cycle
|
|
33
34
|
import { DeviceCommands } from './DeviceCommands';
|
|
35
|
+
import { deviceWalletSessionStore } from './DeviceWalletSessionStore';
|
|
34
36
|
import {
|
|
35
37
|
type DeviceFirmwareRange,
|
|
36
38
|
DeviceModelToTypes,
|
|
@@ -53,6 +55,7 @@ import {
|
|
|
53
55
|
PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
|
|
54
56
|
requestProtocolV2DeviceInfo,
|
|
55
57
|
} from '../protocols/protocol-v2/features';
|
|
58
|
+
import { refreshProtocolV2DeviceStatus } from '../protocols/protocol-v2/walletSession';
|
|
56
59
|
import { buildProtocolV1FeaturesPayload, buildProtocolV2FeaturesPayload } from '../deviceProfile';
|
|
57
60
|
|
|
58
61
|
import type { PROTO } from '../constants';
|
|
@@ -65,6 +68,7 @@ import type { PassphrasePromptResponse } from './DeviceCommands';
|
|
|
65
68
|
import type { Deferred, HardwareConnectProtocol } from '@onekeyfe/hd-shared';
|
|
66
69
|
import type {
|
|
67
70
|
OneKeyDeviceInfo as DeviceDescriptor,
|
|
71
|
+
DeviceStatus,
|
|
68
72
|
ProtocolV2DeviceInfo,
|
|
69
73
|
Success,
|
|
70
74
|
} from '@onekeyfe/hd-transport';
|
|
@@ -76,6 +80,7 @@ export type InitOptions = {
|
|
|
76
80
|
passphraseState?: string;
|
|
77
81
|
deriveCardano?: boolean;
|
|
78
82
|
connectProtocol?: HardwareConnectProtocol;
|
|
83
|
+
protocolV2DeviceInfoTimeoutMs?: number;
|
|
79
84
|
};
|
|
80
85
|
|
|
81
86
|
export type RunOptions = {
|
|
@@ -118,8 +123,6 @@ export interface Device {
|
|
|
118
123
|
emit<K extends keyof DeviceEvents>(type: K, ...args: DeviceEvents[K]): boolean;
|
|
119
124
|
}
|
|
120
125
|
|
|
121
|
-
const deviceSessionCache: Record<string, string> = {};
|
|
122
|
-
|
|
123
126
|
/**
|
|
124
127
|
* Pre-populate the device session cache with a known session ID.
|
|
125
128
|
*
|
|
@@ -137,8 +140,7 @@ export function preloadSessionCache(
|
|
|
137
140
|
passphraseState: string,
|
|
138
141
|
sessionId: string
|
|
139
142
|
): void {
|
|
140
|
-
|
|
141
|
-
deviceSessionCache[key] = sessionId;
|
|
143
|
+
deviceWalletSessionStore.set(deviceId, passphraseState, sessionId);
|
|
142
144
|
}
|
|
143
145
|
|
|
144
146
|
export class Device extends EventEmitter {
|
|
@@ -187,7 +189,7 @@ export class Device extends EventEmitter {
|
|
|
187
189
|
/**
|
|
188
190
|
* 唯一设备状态缓存。
|
|
189
191
|
*
|
|
190
|
-
* V1 直接保存原生 Features;V2 保存由
|
|
192
|
+
* V1 直接保存原生 Features;V2 保存由 DeviceInfoGet 映射出的
|
|
191
193
|
* Features 视图。Device 不再保存 profile,结构化 DeviceProfile 只作为
|
|
192
194
|
* getDeviceInfo() 的 API 返回值存在。
|
|
193
195
|
*/
|
|
@@ -197,7 +199,7 @@ export class Device extends EventEmitter {
|
|
|
197
199
|
* 是否需要更新设备信息。
|
|
198
200
|
*
|
|
199
201
|
* 历史名称保留用于兼容现有调用语义;对 V2 表示 features 需要由
|
|
200
|
-
*
|
|
202
|
+
* DeviceInfoGet 刷新。
|
|
201
203
|
*/
|
|
202
204
|
featuresNeedsReload = false;
|
|
203
205
|
|
|
@@ -264,7 +266,7 @@ export class Device extends EventEmitter {
|
|
|
264
266
|
const connectId = this.getConnectId();
|
|
265
267
|
const deviceId = this.getCurrentDeviceId() || null;
|
|
266
268
|
|
|
267
|
-
const features = this
|
|
269
|
+
const { features } = this;
|
|
268
270
|
|
|
269
271
|
return {
|
|
270
272
|
/** Android uses Mac address, iOS uses uuid, USB uses uuid */
|
|
@@ -326,7 +328,10 @@ export class Device extends EventEmitter {
|
|
|
326
328
|
});
|
|
327
329
|
}
|
|
328
330
|
|
|
329
|
-
async acquire(
|
|
331
|
+
async acquire(
|
|
332
|
+
connectProtocol?: HardwareConnectProtocol,
|
|
333
|
+
options?: { throwOnRunPromiseError?: boolean }
|
|
334
|
+
) {
|
|
330
335
|
const env = DataManager.getSettings('env');
|
|
331
336
|
const mainIdKey = DataManager.isBleConnect(env) ? 'id' : 'session';
|
|
332
337
|
const expectedProtocol = connectProtocol ?? this.originalDescriptor.protocolType;
|
|
@@ -375,6 +380,9 @@ export class Device extends EventEmitter {
|
|
|
375
380
|
|
|
376
381
|
this.commands = new DeviceCommands(this, this.mainId ?? '');
|
|
377
382
|
} catch (error) {
|
|
383
|
+
if (options?.throwOnRunPromiseError) {
|
|
384
|
+
throw error;
|
|
385
|
+
}
|
|
378
386
|
if (this.runPromise) {
|
|
379
387
|
this.runPromise.reject(error);
|
|
380
388
|
} else {
|
|
@@ -477,7 +485,7 @@ export class Device extends EventEmitter {
|
|
|
477
485
|
/**
|
|
478
486
|
* 唯一协议判别器。
|
|
479
487
|
*
|
|
480
|
-
* descriptor.protocolType 是协议探测后的结果;V2 features 由
|
|
488
|
+
* descriptor.protocolType 是协议探测后的结果;V2 features 由 DeviceInfoGet
|
|
481
489
|
* 映射产生。
|
|
482
490
|
* 全 SDK 的协议分支都必须走这里,不要直接读 originalDescriptor.protocolType
|
|
483
491
|
* 或从 features 反推。
|
|
@@ -506,9 +514,8 @@ export class Device extends EventEmitter {
|
|
|
506
514
|
const serialNo = this.getCurrentSerialNo();
|
|
507
515
|
if (serialNo) return serialNo;
|
|
508
516
|
|
|
509
|
-
// connectId 是 SDK 内部连接路由 key
|
|
510
|
-
//
|
|
511
|
-
// features.serialNo / deviceId 的业务语义。
|
|
517
|
+
// connectId 是 SDK 内部连接路由 key。这里兜底到 transport descriptor,
|
|
518
|
+
// 不改变 features.serialNo / deviceId 的业务语义。
|
|
512
519
|
return this.originalDescriptor.path || this.originalDescriptor.id || '';
|
|
513
520
|
}
|
|
514
521
|
|
|
@@ -622,13 +629,6 @@ export class Device extends EventEmitter {
|
|
|
622
629
|
};
|
|
623
630
|
}
|
|
624
631
|
|
|
625
|
-
private generateStateKey(deviceId: string, passphraseState?: string) {
|
|
626
|
-
if (passphraseState) {
|
|
627
|
-
return `${deviceId}@${passphraseState}`;
|
|
628
|
-
}
|
|
629
|
-
return deviceId;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
632
|
private getSessionCacheDeviceKey(_deviceId?: string) {
|
|
633
633
|
const deviceId = _deviceId || this.getCurrentDeviceId();
|
|
634
634
|
if (deviceId) return deviceId;
|
|
@@ -639,7 +639,6 @@ export class Device extends EventEmitter {
|
|
|
639
639
|
}
|
|
640
640
|
|
|
641
641
|
getInternalState(_deviceId?: string) {
|
|
642
|
-
Log.debug('getInternalState session cache: ', deviceSessionCache);
|
|
643
642
|
Log.debug(
|
|
644
643
|
'getInternalState session param: ',
|
|
645
644
|
`device_id: ${_deviceId}`,
|
|
@@ -657,8 +656,7 @@ export class Device extends EventEmitter {
|
|
|
657
656
|
// downstream call carries it and hits the primary lookup below.
|
|
658
657
|
if (!this.passphraseState) return undefined;
|
|
659
658
|
|
|
660
|
-
|
|
661
|
-
return deviceSessionCache[usePassKey];
|
|
659
|
+
return deviceWalletSessionStore.get(deviceId, this.passphraseState);
|
|
662
660
|
}
|
|
663
661
|
|
|
664
662
|
// attach to pin to fix internal state
|
|
@@ -674,36 +672,26 @@ export class Device extends EventEmitter {
|
|
|
674
672
|
`device_id: ${deviceId}`,
|
|
675
673
|
`enablePassphrase: ${enablePassphrase}`,
|
|
676
674
|
`passphraseState: ${passphraseState}`,
|
|
677
|
-
`
|
|
678
|
-
`
|
|
675
|
+
`hasSessionId: ${Boolean(sessionId)}`,
|
|
676
|
+
`hasFeaturesSessionId: ${Boolean(featuresSessionId)}`
|
|
679
677
|
);
|
|
680
678
|
|
|
681
679
|
const cacheDeviceKey = this.getSessionCacheDeviceKey(deviceId);
|
|
682
680
|
if (!cacheDeviceKey) return;
|
|
683
681
|
|
|
684
682
|
if (enablePassphrase) {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
} else if (featuresSessionId) {
|
|
689
|
-
deviceSessionCache[this.generateStateKey(cacheDeviceKey, passphraseState)] =
|
|
690
|
-
featuresSessionId;
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// delete the old sessionId
|
|
695
|
-
const oldKey = `${cacheDeviceKey}`;
|
|
696
|
-
if (deviceSessionCache[oldKey]) {
|
|
697
|
-
delete deviceSessionCache[oldKey];
|
|
683
|
+
const walletSessionId =
|
|
684
|
+
sessionId || featuresSessionId || deviceWalletSessionStore.getPending(cacheDeviceKey);
|
|
685
|
+
deviceWalletSessionStore.set(cacheDeviceKey, passphraseState, walletSessionId ?? undefined);
|
|
698
686
|
}
|
|
699
687
|
|
|
700
|
-
|
|
688
|
+
deviceWalletSessionStore.deletePending(cacheDeviceKey);
|
|
701
689
|
}
|
|
702
690
|
|
|
703
691
|
private setInternalState(state: string, initSession?: boolean) {
|
|
704
692
|
Log.debug(
|
|
705
693
|
'setInternalState session param: ',
|
|
706
|
-
`
|
|
694
|
+
`hasState: ${Boolean(state)}`,
|
|
707
695
|
`initSession: ${initSession}`,
|
|
708
696
|
`deviceId: ${this.features?.deviceId}`,
|
|
709
697
|
`passphraseState: ${this.passphraseState}`
|
|
@@ -714,12 +702,11 @@ export class Device extends EventEmitter {
|
|
|
714
702
|
const deviceId = this.getSessionCacheDeviceKey();
|
|
715
703
|
if (!deviceId) return;
|
|
716
704
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
if (
|
|
720
|
-
|
|
705
|
+
if (this.passphraseState) {
|
|
706
|
+
deviceWalletSessionStore.set(deviceId, this.passphraseState, state);
|
|
707
|
+
} else if (initSession) {
|
|
708
|
+
deviceWalletSessionStore.setPending(deviceId, state);
|
|
721
709
|
}
|
|
722
|
-
Log.debug('setInternalState done session cache: ', deviceSessionCache);
|
|
723
710
|
}
|
|
724
711
|
|
|
725
712
|
clearInternalState(_deviceId?: string) {
|
|
@@ -727,12 +714,10 @@ export class Device extends EventEmitter {
|
|
|
727
714
|
|
|
728
715
|
const deviceId = this.getSessionCacheDeviceKey(_deviceId);
|
|
729
716
|
if (!deviceId) return;
|
|
730
|
-
|
|
731
|
-
delete deviceSessionCache[key];
|
|
717
|
+
deviceWalletSessionStore.deletePending(deviceId);
|
|
732
718
|
|
|
733
719
|
if (this.passphraseState) {
|
|
734
|
-
|
|
735
|
-
delete deviceSessionCache[usePassKey];
|
|
720
|
+
deviceWalletSessionStore.delete(deviceId, this.passphraseState);
|
|
736
721
|
}
|
|
737
722
|
}
|
|
738
723
|
|
|
@@ -741,13 +726,16 @@ export class Device extends EventEmitter {
|
|
|
741
726
|
if (this.isProtocolV2()) {
|
|
742
727
|
this.passphraseState = options?.passphraseState;
|
|
743
728
|
if (this.features && !this.featuresNeedsReload && !options?.initSession) {
|
|
729
|
+
if (this.features.bootloaderMode) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
744
732
|
// 不能直接信任缓存 features:设备端 wipe / 完成初始化 / 改 label 后
|
|
745
733
|
// features 会永久陈旧。每次 run 做一次轻量 status 刷新(不含 fw/SE),
|
|
746
734
|
// 用字段级合并保留已有版本和 SE 信息。
|
|
747
|
-
await this._refreshProtocolV2Status();
|
|
735
|
+
await this._refreshProtocolV2Status(options);
|
|
748
736
|
return;
|
|
749
737
|
}
|
|
750
|
-
await this._initializeProtocolV2();
|
|
738
|
+
await this._initializeProtocolV2(options);
|
|
751
739
|
return;
|
|
752
740
|
}
|
|
753
741
|
|
|
@@ -797,10 +785,10 @@ export class Device extends EventEmitter {
|
|
|
797
785
|
/**
|
|
798
786
|
* Device initialization over Protocol V2.
|
|
799
787
|
*
|
|
800
|
-
* Protocol V2 不走传统 Initialize/GetFeatures;直接用
|
|
788
|
+
* Protocol V2 不走传统 Initialize/GetFeatures;直接用 DeviceInfoGet
|
|
801
789
|
* 生成唯一的 features 状态。
|
|
802
790
|
*/
|
|
803
|
-
private async _initializeProtocolV2() {
|
|
791
|
+
private async _initializeProtocolV2(options?: InitOptions) {
|
|
804
792
|
Log.debug('Initialize device via Protocol V2 features adapter');
|
|
805
793
|
|
|
806
794
|
try {
|
|
@@ -809,6 +797,7 @@ export class Device extends EventEmitter {
|
|
|
809
797
|
// 且 reject 后底层调用仍会残留。
|
|
810
798
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
811
799
|
commands: this.commands,
|
|
800
|
+
timeoutMs: options?.protocolV2DeviceInfoTimeoutMs,
|
|
812
801
|
});
|
|
813
802
|
// 默认请求不含 SE/hash 数据,scope 如实标注为 basic;
|
|
814
803
|
// 完整数据由 getDeviceInfo(scope:'verify'|'full') 获取。
|
|
@@ -824,15 +813,16 @@ export class Device extends EventEmitter {
|
|
|
824
813
|
/**
|
|
825
814
|
* Protocol V2 的轻量状态刷新(每次 run 前调用)。
|
|
826
815
|
*
|
|
827
|
-
* 请求 hw +
|
|
828
|
-
*
|
|
816
|
+
* 请求 hw + coprocessor + status(不含 fw/SE target):status 提供 init_states / label /
|
|
817
|
+
* passphrase_enabled 等会在设备端变化的字段;hw/coprocessor 提供 serialNo / bleName。
|
|
829
818
|
* versions 为空时按字段级合并保留旧值,verify 数据不会被降级。
|
|
830
819
|
*/
|
|
831
|
-
private async _refreshProtocolV2Status() {
|
|
820
|
+
private async _refreshProtocolV2Status(options?: InitOptions) {
|
|
832
821
|
try {
|
|
833
822
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
834
823
|
commands: this.commands,
|
|
835
824
|
request: PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
|
|
825
|
+
timeoutMs: options?.protocolV2DeviceInfoTimeoutMs,
|
|
836
826
|
});
|
|
837
827
|
const features = this.updateProtocolV2Features(deviceInfo);
|
|
838
828
|
Log.debug('Protocol V2 features (status refresh):', features);
|
|
@@ -856,6 +846,7 @@ export class Device extends EventEmitter {
|
|
|
856
846
|
}
|
|
857
847
|
|
|
858
848
|
_updateFeatures(protoFeatures: PROTO.Features | Features, initSession?: boolean) {
|
|
849
|
+
const previousCacheDeviceKey = this.getSessionCacheDeviceKey();
|
|
859
850
|
let feat =
|
|
860
851
|
'protocol' in protoFeatures
|
|
861
852
|
? protoFeatures
|
|
@@ -865,28 +856,50 @@ export class Device extends EventEmitter {
|
|
|
865
856
|
if (this.features?.sessionId && !feat.sessionId) {
|
|
866
857
|
feat.sessionId = this.features.sessionId;
|
|
867
858
|
}
|
|
868
|
-
if (this.getCurrentDeviceId() && feat.sessionId) {
|
|
869
|
-
this.setInternalState(feat.sessionId, initSession);
|
|
870
|
-
}
|
|
871
859
|
feat.unlocked = feat.unlocked ?? true;
|
|
872
860
|
|
|
873
861
|
feat = fixFeaturesFirmwareVersion(feat);
|
|
874
862
|
|
|
875
863
|
this.features = feat;
|
|
864
|
+
const nextCacheDeviceKey = this.getSessionCacheDeviceKey();
|
|
865
|
+
if (previousCacheDeviceKey && nextCacheDeviceKey) {
|
|
866
|
+
deviceWalletSessionStore.migrateDeviceKey(previousCacheDeviceKey, nextCacheDeviceKey);
|
|
867
|
+
}
|
|
868
|
+
if (feat.deviceId && feat.sessionId) {
|
|
869
|
+
this.setInternalState(feat.sessionId, initSession);
|
|
870
|
+
}
|
|
876
871
|
this.featuresNeedsReload = false;
|
|
877
872
|
this.emit(DEVICE.FEATURES, this, feat);
|
|
878
873
|
}
|
|
879
874
|
|
|
880
875
|
updateProtocolV2Features(deviceInfo?: ProtocolV2DeviceInfo) {
|
|
876
|
+
const previousCacheDeviceKey = this.getSessionCacheDeviceKey();
|
|
881
877
|
const features = fixFeaturesFirmwareVersion(
|
|
882
878
|
buildProtocolV2FeaturesPayload(deviceInfo, this.features)
|
|
883
879
|
);
|
|
884
880
|
this.features = features;
|
|
881
|
+
const nextCacheDeviceKey = this.getSessionCacheDeviceKey();
|
|
882
|
+
if (previousCacheDeviceKey && nextCacheDeviceKey) {
|
|
883
|
+
deviceWalletSessionStore.migrateDeviceKey(previousCacheDeviceKey, nextCacheDeviceKey);
|
|
884
|
+
}
|
|
885
885
|
this.featuresNeedsReload = false;
|
|
886
886
|
this.emit(DEVICE.FEATURES, this, features);
|
|
887
887
|
return features;
|
|
888
888
|
}
|
|
889
889
|
|
|
890
|
+
updateProtocolV2Status(status: DeviceStatus) {
|
|
891
|
+
const previousDeviceInfo = this.features?.raw?.protocolV2DeviceInfo;
|
|
892
|
+
const previousStatus = previousDeviceInfo?.status;
|
|
893
|
+
return this.updateProtocolV2Features({
|
|
894
|
+
...(previousDeviceInfo ?? {}),
|
|
895
|
+
protocol_version: previousDeviceInfo?.protocol_version ?? this.features?.protocolVersion ?? 2,
|
|
896
|
+
status: {
|
|
897
|
+
...previousStatus,
|
|
898
|
+
...status,
|
|
899
|
+
},
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
|
|
890
903
|
/**
|
|
891
904
|
* 暂时只在 acquire 后更新 Session ID
|
|
892
905
|
* 后续看是否有需要依据 listen 返回结果更新
|
|
@@ -1167,8 +1180,7 @@ export class Device extends EventEmitter {
|
|
|
1167
1180
|
}
|
|
1168
1181
|
|
|
1169
1182
|
supportUnlockVersionRange(): DeviceFirmwareRange {
|
|
1170
|
-
// 仅适用于 Protocol V1 的 Pro 系列;Pro2
|
|
1171
|
-
// 且 Protocol V2 固件从首个版本即支持 UnLockDevice(见 unlockDevice 的 isProtocolV2 短路)。
|
|
1183
|
+
// 仅适用于 Protocol V1 的 Pro 系列;Pro2 走独立的 Protocol V2 解锁流程。
|
|
1172
1184
|
return {
|
|
1173
1185
|
pro: {
|
|
1174
1186
|
min: '4.15.0',
|
|
@@ -1177,6 +1189,23 @@ export class Device extends EventEmitter {
|
|
|
1177
1189
|
}
|
|
1178
1190
|
|
|
1179
1191
|
async unlockDevice() {
|
|
1192
|
+
if (this.isProtocolV2()) {
|
|
1193
|
+
try {
|
|
1194
|
+
await this.commands.typedCall('DeviceSessionAskPin', 'DeviceSessionPinResult');
|
|
1195
|
+
} catch (error) {
|
|
1196
|
+
const errorText =
|
|
1197
|
+
error instanceof Error
|
|
1198
|
+
? `${error.name} ${error.message}`
|
|
1199
|
+
: String((error as { message?: unknown } | null)?.message ?? error ?? '');
|
|
1200
|
+
if (errorText.includes('Failure_UnexpectedMessage')) {
|
|
1201
|
+
throw createDeviceNotSupportMethodError('deviceUnlock', this.getCurrentFirmwareType());
|
|
1202
|
+
}
|
|
1203
|
+
throw error;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
return refreshProtocolV2DeviceStatus(this);
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1180
1209
|
const firmwareVersion = this.getCurrentFirmwareVersionString() ?? '0.0.0';
|
|
1181
1210
|
const versionRange = this.getCurrentMethodVersionRange(
|
|
1182
1211
|
type => this.supportUnlockVersionRange()[type]
|
|
@@ -1186,9 +1215,7 @@ export class Device extends EventEmitter {
|
|
|
1186
1215
|
this.features,
|
|
1187
1216
|
Enum_Capability.Capability_AttachToPin
|
|
1188
1217
|
);
|
|
1189
|
-
// Pro2 (Protocol V2) 版本线独立于 Pro 系列,固件从首个版本即支持 UnLockDevice
|
|
1190
1218
|
const supportUnlock =
|
|
1191
|
-
this.isProtocolV2() ||
|
|
1192
1219
|
supportAttachPinCapability ||
|
|
1193
1220
|
(versionRange &&
|
|
1194
1221
|
semver.valid(firmwareVersion) &&
|
|
@@ -1210,14 +1237,6 @@ export class Device extends EventEmitter {
|
|
|
1210
1237
|
return Promise.resolve(features);
|
|
1211
1238
|
}
|
|
1212
1239
|
|
|
1213
|
-
// legacy 解锁探测仅适用于 Protocol V1 老固件;V2 固件必然支持 UnLockDevice
|
|
1214
|
-
if (this.isProtocolV2()) {
|
|
1215
|
-
throw ERRORS.TypedError(
|
|
1216
|
-
HardwareErrorCode.RuntimeError,
|
|
1217
|
-
'unlock device error: device firmware does not support UnLockDevice'
|
|
1218
|
-
);
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
1240
|
const { type } = await this.commands.typedCall('GetAddress', 'Address', {
|
|
1222
1241
|
address_n: [toHardened(44), toHardened(1), toHardened(0), 0, 0],
|
|
1223
1242
|
coin_name: 'Testnet',
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export class DeviceWalletSessionStore {
|
|
2
|
+
private readonly walletSessions = new Map<string, Map<string, string>>();
|
|
3
|
+
|
|
4
|
+
private readonly pendingSessions = new Map<string, string>();
|
|
5
|
+
|
|
6
|
+
get(deviceKey: string, passphraseState?: string) {
|
|
7
|
+
if (!deviceKey || !passphraseState) return undefined;
|
|
8
|
+
return this.walletSessions.get(deviceKey)?.get(passphraseState);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
set(deviceKey: string, passphraseState?: string, sessionId?: string) {
|
|
12
|
+
if (!deviceKey || !passphraseState || !sessionId) return;
|
|
13
|
+
let deviceSessions = this.walletSessions.get(deviceKey);
|
|
14
|
+
if (!deviceSessions) {
|
|
15
|
+
deviceSessions = new Map<string, string>();
|
|
16
|
+
this.walletSessions.set(deviceKey, deviceSessions);
|
|
17
|
+
}
|
|
18
|
+
deviceSessions.set(passphraseState, sessionId);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setPending(deviceKey: string, sessionId?: string) {
|
|
22
|
+
if (!deviceKey || !sessionId) return;
|
|
23
|
+
this.pendingSessions.set(deviceKey, sessionId);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getPending(deviceKey: string) {
|
|
27
|
+
if (!deviceKey) return undefined;
|
|
28
|
+
return this.pendingSessions.get(deviceKey);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
delete(deviceKey: string, passphraseState?: string) {
|
|
32
|
+
if (!deviceKey || !passphraseState) return;
|
|
33
|
+
const deviceSessions = this.walletSessions.get(deviceKey);
|
|
34
|
+
if (!deviceSessions) return;
|
|
35
|
+
deviceSessions.delete(passphraseState);
|
|
36
|
+
if (deviceSessions.size === 0) {
|
|
37
|
+
this.walletSessions.delete(deviceKey);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
deletePending(deviceKey: string) {
|
|
42
|
+
if (!deviceKey) return;
|
|
43
|
+
this.pendingSessions.delete(deviceKey);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
deleteDevice(deviceKey: string) {
|
|
47
|
+
if (!deviceKey) return;
|
|
48
|
+
this.walletSessions.delete(deviceKey);
|
|
49
|
+
this.pendingSessions.delete(deviceKey);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
migrateDeviceKey(from: string, to: string) {
|
|
53
|
+
if (!from || !to || from === to) return;
|
|
54
|
+
|
|
55
|
+
const sourceSessions = this.walletSessions.get(from);
|
|
56
|
+
if (sourceSessions) {
|
|
57
|
+
const targetSessions = this.walletSessions.get(to) ?? new Map<string, string>();
|
|
58
|
+
this.walletSessions.set(to, targetSessions);
|
|
59
|
+
sourceSessions.forEach((sessionId, passphraseState) => {
|
|
60
|
+
targetSessions.set(passphraseState, sessionId);
|
|
61
|
+
});
|
|
62
|
+
this.walletSessions.delete(from);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const pendingSession = this.pendingSessions.get(from);
|
|
66
|
+
if (pendingSession) {
|
|
67
|
+
this.pendingSessions.set(to, pendingSession);
|
|
68
|
+
this.pendingSessions.delete(from);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
clear() {
|
|
73
|
+
this.walletSessions.clear();
|
|
74
|
+
this.pendingSessions.clear();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const deviceWalletSessionStore = new DeviceWalletSessionStore();
|