@onekeyfe/hd-core 1.2.0-alpha.18 → 1.2.0-alpha.19
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__/device-features-state.test.ts +78 -0
- package/__tests__/device-identity.test.ts +30 -0
- package/__tests__/device-pool-state.test.ts +113 -0
- package/__tests__/device-settings.test.ts +108 -0
- package/__tests__/device-state-contract.test.ts +27 -0
- package/__tests__/device-state-events.test.ts +163 -0
- package/__tests__/device-state-mapper.test.ts +222 -0
- package/__tests__/device-state-projector.test.ts +94 -0
- package/__tests__/device-state-store.test.ts +115 -0
- package/__tests__/device-wallet-session-store.test.ts +46 -0
- package/__tests__/deviceFeaturesUtils.test.ts +13 -1
- package/__tests__/deviceSettings.test.ts +54 -0
- package/__tests__/get-device-state.test.ts +367 -0
- package/__tests__/logBlockEvent.test.ts +13 -0
- package/__tests__/protocol-v2.test.ts +358 -413
- package/__tests__/public-device-state-api.test.ts +153 -0
- package/__tests__/refresh-device-state.test.ts +101 -0
- package/__tests__/request-context-logging.test.ts +16 -0
- package/__tests__/search-devices.test.ts +93 -0
- package/dist/api/ClearSessionCache.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetDeviceState.d.ts +7 -0
- package/dist/api/GetDeviceState.d.ts.map +1 -0
- package/dist/api/GetFeatures.d.ts +1 -1
- package/dist/api/GetFeatures.d.ts.map +1 -1
- package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
- package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
- package/dist/api/device/DeviceSettings.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -4
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/internal.d.ts +4 -0
- package/dist/api/internal.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts +0 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceSettingsGet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -1
- package/dist/api/utils.d.ts +2 -1
- package/dist/api/utils.d.ts.map +1 -1
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +22 -3
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceFeaturesState.d.ts +4 -0
- package/dist/device/DeviceFeaturesState.d.ts.map +1 -0
- package/dist/device/DevicePool.d.ts +1 -0
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts +13 -0
- package/dist/device/DeviceStateMapper.d.ts.map +1 -0
- package/dist/device/DeviceStateProjector.d.ts +8 -0
- package/dist/device/DeviceStateProjector.d.ts.map +1 -0
- package/dist/device/DeviceStateStore.d.ts +23 -0
- package/dist/device/DeviceStateStore.d.ts.map +1 -0
- package/dist/device/deviceIdentity.d.ts +5 -0
- package/dist/device/deviceIdentity.d.ts.map +1 -0
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/deviceProfile/index.d.ts +0 -1
- package/dist/deviceProfile/index.d.ts.map +1 -1
- package/dist/events/device.d.ts +7 -2
- package/dist/events/device.d.ts.map +1 -1
- package/dist/index.d.ts +688 -639
- package/dist/index.js +1274 -942
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/features.d.ts +1 -0
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +1 -1
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/deviceSettings.d.ts +8 -0
- package/dist/types/api/deviceSettings.d.ts.map +1 -1
- package/dist/types/api/getDeviceState.d.ts +12 -0
- package/dist/types/api/getDeviceState.d.ts.map +1 -0
- package/dist/types/api/index.d.ts +4 -7
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +1 -6
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +121 -1
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/deviceSettings.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/tracing.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/ClearSessionCache.ts +4 -0
- package/src/api/FirmwareUpdateV4.ts +15 -7
- package/src/api/GetDeviceState.ts +55 -0
- package/src/api/GetFeatures.ts +8 -3
- package/src/api/GetOnekeyFeatures.ts +3 -72
- package/src/api/GetPassphraseState.ts +3 -1
- package/src/api/SearchDevices.ts +25 -4
- package/src/api/device/DeviceRebootToBoardloader.ts +1 -0
- package/src/api/device/DeviceRebootToBootloader.ts +1 -0
- package/src/api/device/DeviceSettings.ts +28 -0
- package/src/api/index.ts +1 -4
- package/src/api/internal.ts +9 -0
- package/src/api/protocol-v2/DeviceInfoGet.ts +3 -5
- package/src/api/protocol-v2/DeviceReboot.ts +3 -1
- package/src/api/protocol-v2/DeviceSettingsGet.ts +3 -1
- package/src/api/protocol-v2/DeviceSettingsSet.ts +2 -0
- package/src/api/protocol-v2/DeviceStatusGet.ts +1 -0
- package/src/api/utils.ts +9 -3
- package/src/constants/index.ts +1 -4
- package/src/core/index.ts +7 -1
- package/src/data/messages/messages-protocol-v2.json +22 -13
- package/src/device/Device.ts +256 -78
- package/src/device/DeviceFeaturesState.ts +34 -0
- package/src/device/DevicePool.ts +35 -9
- package/src/device/DeviceStateMapper.ts +364 -0
- package/src/device/DeviceStateProjector.ts +92 -0
- package/src/device/DeviceStateStore.ts +242 -0
- package/src/device/deviceIdentity.ts +18 -0
- package/src/deviceProfile/buildDeviceFeatures.ts +166 -109
- package/src/deviceProfile/index.ts +0 -1
- package/src/events/device.ts +13 -1
- package/src/events/logBlockEvent.ts +1 -1
- package/src/inject.ts +4 -5
- package/src/protocols/protocol-v2/features.ts +18 -9
- package/src/protocols/protocol-v2/index.ts +1 -0
- package/src/protocols/protocol-v2/walletSession.ts +5 -7
- package/src/types/api/deviceSettings.ts +8 -0
- package/src/types/api/getDeviceState.ts +19 -0
- package/src/types/api/index.ts +3 -20
- package/src/types/api/protocolV2.ts +0 -24
- package/src/types/device.ts +148 -1
- package/src/utils/deviceFeaturesUtils.ts +5 -1
- package/src/utils/deviceSettings.ts +41 -0
- package/src/utils/tracing.ts +3 -1
- package/dist/api/GetDeviceInfo.d.ts +0 -9
- package/dist/api/GetDeviceInfo.d.ts.map +0 -1
- package/dist/deviceProfile/buildDeviceProfile.d.ts +0 -22
- package/dist/deviceProfile/buildDeviceProfile.d.ts.map +0 -1
- package/dist/types/api/getDeviceInfo.d.ts +0 -88
- package/dist/types/api/getDeviceInfo.d.ts.map +0 -1
- package/src/api/GetDeviceInfo.ts +0 -152
- package/src/deviceProfile/buildDeviceProfile.ts +0 -370
- package/src/types/api/getDeviceInfo.ts +0 -106
package/src/device/Device.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
import semver from 'semver';
|
|
3
|
-
import { Enum_Capability } from '@onekeyfe/hd-transport';
|
|
3
|
+
import { DeviceRebootType, Enum_Capability } from '@onekeyfe/hd-transport';
|
|
4
4
|
import {
|
|
5
5
|
EDeviceType,
|
|
6
6
|
ERRORS,
|
|
@@ -32,10 +32,23 @@ import {
|
|
|
32
32
|
import { generateInstanceId } from '../utils/tracing';
|
|
33
33
|
// eslint-disable-next-line import/no-cycle
|
|
34
34
|
import { DeviceCommands } from './DeviceCommands';
|
|
35
|
+
import { mergeDeviceFeaturesPatch } from './DeviceFeaturesState';
|
|
36
|
+
import {
|
|
37
|
+
mapDeviceSettingsToState,
|
|
38
|
+
mapFeaturesToState,
|
|
39
|
+
mapProtocolV1OnekeyFeaturesToState,
|
|
40
|
+
mapProtocolV2DeviceInfoToState,
|
|
41
|
+
mapProtocolV2DeviceStatusToState,
|
|
42
|
+
} from './DeviceStateMapper';
|
|
43
|
+
import { projectFeatures } from './DeviceStateProjector';
|
|
44
|
+
import { DeviceStateStore, createPublicDeviceState } from './DeviceStateStore';
|
|
35
45
|
import { deviceWalletSessionStore } from './DeviceWalletSessionStore';
|
|
36
46
|
import {
|
|
37
47
|
type DeviceFirmwareRange,
|
|
38
48
|
DeviceModelToTypes,
|
|
49
|
+
type DeviceStateEvent,
|
|
50
|
+
type DeviceStatePatch,
|
|
51
|
+
type DeviceStateUpdateSource,
|
|
39
52
|
DeviceTypeToModels,
|
|
40
53
|
type Device as DeviceTyped,
|
|
41
54
|
EOneKeyDeviceMode,
|
|
@@ -52,12 +65,16 @@ import TransportManager from '../data-manager/TransportManager';
|
|
|
52
65
|
import { toHardened } from '../api/helpers/pathUtils';
|
|
53
66
|
import { existCapability } from '../utils/capabilitieUtils';
|
|
54
67
|
import {
|
|
68
|
+
PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST,
|
|
69
|
+
PROTOCOL_V2_FULL_DEVICE_INFO_REQUEST,
|
|
70
|
+
PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
55
71
|
requestProtocolV2DeviceInfo,
|
|
56
72
|
requestProtocolV2DeviceStatus,
|
|
57
73
|
} from '../protocols/protocol-v2/features';
|
|
58
|
-
import { buildProtocolV1FeaturesPayload
|
|
74
|
+
import { buildProtocolV1FeaturesPayload } from '../deviceProfile';
|
|
59
75
|
|
|
60
76
|
import type { PROTO } from '../constants';
|
|
77
|
+
import type { DeviceStateReadOptions } from '../types/api/getDeviceState';
|
|
61
78
|
import type {
|
|
62
79
|
DeviceButtonRequestPayload,
|
|
63
80
|
DeviceFeaturesPayload,
|
|
@@ -80,6 +97,8 @@ export type InitOptions = {
|
|
|
80
97
|
deriveCardano?: boolean;
|
|
81
98
|
connectProtocol?: HardwareConnectProtocol;
|
|
82
99
|
protocolV2DeviceInfoTimeoutMs?: number;
|
|
100
|
+
/** 设备发现内部使用:返回列表前刷新 Protocol V2 运行时状态。 */
|
|
101
|
+
refreshRuntimeState?: boolean;
|
|
83
102
|
};
|
|
84
103
|
|
|
85
104
|
export type RunOptions = {
|
|
@@ -100,6 +119,7 @@ export interface DeviceEvents {
|
|
|
100
119
|
[DEVICE.ATTACH_PIN_ON_DEVICE]: [Device, PassphraseRequestPayload?];
|
|
101
120
|
[DEVICE.BUTTON]: [Device, DeviceButtonRequestPayload];
|
|
102
121
|
[DEVICE.FEATURES]: [Device, DeviceFeaturesPayload];
|
|
122
|
+
[DEVICE.STATE]: [Device, DeviceStateEvent];
|
|
103
123
|
[DEVICE.PASSPHRASE]: [
|
|
104
124
|
Device,
|
|
105
125
|
PassphraseRequestPayload,
|
|
@@ -186,22 +206,26 @@ export class Device extends EventEmitter {
|
|
|
186
206
|
*/
|
|
187
207
|
private deviceAcquired = false;
|
|
188
208
|
|
|
189
|
-
/**
|
|
190
|
-
|
|
191
|
-
*
|
|
192
|
-
* V1 直接保存原生 Features;V2 保存由 DeviceInfoGet 映射出的
|
|
193
|
-
* Features 视图。Device 不再保存 profile,结构化 DeviceProfile 只作为
|
|
194
|
-
* getDeviceInfo() 的 API 返回值存在。
|
|
195
|
-
*/
|
|
196
|
-
features: Features | undefined = undefined;
|
|
209
|
+
/** 唯一设备状态缓存。旧 Features 仅通过 getter/setter 做兼容投影。 */
|
|
210
|
+
private stateStore = new DeviceStateStore();
|
|
197
211
|
|
|
198
|
-
/**
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
212
|
+
/** 物理重连或重启后,下一次初始化必须重新读取 DeviceInfo。 */
|
|
213
|
+
private protocolV2StateNeedsReload = false;
|
|
214
|
+
|
|
215
|
+
get state() {
|
|
216
|
+
return this.stateStore.getState();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
get features(): Features | undefined {
|
|
220
|
+
return this.state ? projectFeatures(this.state) : undefined;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
set features(features: Features | undefined) {
|
|
224
|
+
this.stateStore = new DeviceStateStore();
|
|
225
|
+
if (features) {
|
|
226
|
+
this.stateStore.update(mapFeaturesToState(features), 'compatibility');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
205
229
|
|
|
206
230
|
runPromise?: Deferred<void> | null;
|
|
207
231
|
|
|
@@ -267,6 +291,7 @@ export class Device extends EventEmitter {
|
|
|
267
291
|
const deviceId = this.getCurrentDeviceId() || null;
|
|
268
292
|
|
|
269
293
|
const { features } = this;
|
|
294
|
+
const state = this.state ? createPublicDeviceState(this.state) : undefined;
|
|
270
295
|
|
|
271
296
|
return {
|
|
272
297
|
/** Android uses Mac address, iOS uses uuid, USB uses uuid */
|
|
@@ -283,9 +308,11 @@ export class Device extends EventEmitter {
|
|
|
283
308
|
path: this.originalDescriptor?.path,
|
|
284
309
|
bleName,
|
|
285
310
|
name: bleName || label || `OneKey ${deviceType?.toUpperCase()}`,
|
|
311
|
+
displayName: label || bleName || `OneKey ${deviceType?.toUpperCase()}`,
|
|
286
312
|
label: label || 'OneKey',
|
|
287
313
|
mode: this.getMode(),
|
|
288
314
|
features,
|
|
315
|
+
state,
|
|
289
316
|
sessionId: this.features?.sessionId ?? null,
|
|
290
317
|
firmwareVersion: this.getFirmwareVersion(),
|
|
291
318
|
bleFirmwareVersion: this.getBLEFirmwareVersion(),
|
|
@@ -725,15 +752,16 @@ export class Device extends EventEmitter {
|
|
|
725
752
|
// Protocol V2 不支持传统 Initialize,直接使用协议专用初始化流程。
|
|
726
753
|
if (this.isProtocolV2()) {
|
|
727
754
|
this.passphraseState = options?.passphraseState;
|
|
728
|
-
if (this.
|
|
729
|
-
if (this.features
|
|
755
|
+
if (this.state && !options?.initSession && !this.protocolV2StateNeedsReload) {
|
|
756
|
+
if (this.features?.bootloaderMode) {
|
|
730
757
|
return;
|
|
731
758
|
}
|
|
732
|
-
// 普通业务调用复用缓存。动态状态由显式
|
|
759
|
+
// 普通业务调用复用缓存。动态状态由显式 getDeviceState refresh、
|
|
733
760
|
// 解锁流程和设备事件刷新,避免每个 SDK 方法都额外轮询 DeviceStatus。
|
|
734
761
|
return;
|
|
735
762
|
}
|
|
736
763
|
await this._initializeProtocolV2(options);
|
|
764
|
+
this.protocolV2StateNeedsReload = false;
|
|
737
765
|
return;
|
|
738
766
|
}
|
|
739
767
|
|
|
@@ -797,17 +825,10 @@ export class Device extends EventEmitter {
|
|
|
797
825
|
commands: this.commands,
|
|
798
826
|
timeoutMs: options?.protocolV2DeviceInfoTimeoutMs,
|
|
799
827
|
});
|
|
800
|
-
const deviceStatus = await requestProtocolV2DeviceStatus({
|
|
801
|
-
commands: this.commands,
|
|
802
|
-
}).catch(error => {
|
|
803
|
-
Log.debug('Protocol V2 status unavailable during initialization:', error);
|
|
804
|
-
return undefined;
|
|
805
|
-
});
|
|
806
828
|
// 默认请求不含 SE/hash 数据,scope 如实标注为 basic;
|
|
807
|
-
//
|
|
808
|
-
const features = this.updateProtocolV2Features(deviceInfo,
|
|
829
|
+
// 完整版本与校验数据由公共 getDeviceState({ scope: 'firmware' }) 显式获取。
|
|
830
|
+
const features = this.updateProtocolV2Features(deviceInfo, null);
|
|
809
831
|
Log.debug('Protocol V2 features:', features);
|
|
810
|
-
this.featuresNeedsReload = false;
|
|
811
832
|
} catch (error) {
|
|
812
833
|
Log.error('Protocol V2 initialization failed:', error);
|
|
813
834
|
throw error;
|
|
@@ -819,10 +840,7 @@ export class Device extends EventEmitter {
|
|
|
819
840
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
820
841
|
commands: this.commands,
|
|
821
842
|
});
|
|
822
|
-
|
|
823
|
-
commands: this.commands,
|
|
824
|
-
}).catch(() => undefined);
|
|
825
|
-
return this.updateProtocolV2Features(deviceInfo, deviceStatus);
|
|
843
|
+
return this.updateProtocolV2Features(deviceInfo, null);
|
|
826
844
|
}
|
|
827
845
|
|
|
828
846
|
const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
|
|
@@ -830,6 +848,70 @@ export class Device extends EventEmitter {
|
|
|
830
848
|
return this.features;
|
|
831
849
|
}
|
|
832
850
|
|
|
851
|
+
async getDeviceState(params: DeviceStateReadOptions = {}) {
|
|
852
|
+
const refresh = new Set(params.refreshSections ?? []);
|
|
853
|
+
const getProtocolV2DeviceInfoRequest = () => {
|
|
854
|
+
if (refresh.has('verification')) return PROTOCOL_V2_FULL_DEVICE_INFO_REQUEST;
|
|
855
|
+
if (refresh.has('versions')) return PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST;
|
|
856
|
+
return PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST;
|
|
857
|
+
};
|
|
858
|
+
let initializedWithDeviceInfo = false;
|
|
859
|
+
|
|
860
|
+
if (!this.state) {
|
|
861
|
+
if (this.isProtocolV2()) {
|
|
862
|
+
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
863
|
+
commands: this.commands,
|
|
864
|
+
request: getProtocolV2DeviceInfoRequest(),
|
|
865
|
+
});
|
|
866
|
+
this.updateState(mapProtocolV2DeviceInfoToState(deviceInfo), 'device-info');
|
|
867
|
+
initializedWithDeviceInfo = true;
|
|
868
|
+
} else {
|
|
869
|
+
await this.getFeatures();
|
|
870
|
+
}
|
|
871
|
+
} else if (!this.isProtocolV2() && refresh.size > 0) {
|
|
872
|
+
await this.getFeatures();
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
if (!this.isProtocolV2() && refresh.has('verification')) {
|
|
876
|
+
const { message } = await this.commands.typedCall('OnekeyGetFeatures', 'OnekeyFeatures');
|
|
877
|
+
this.updateState(mapProtocolV1OnekeyFeaturesToState(message), 'device-info');
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
if (this.isProtocolV2()) {
|
|
881
|
+
const refreshDeviceInfo =
|
|
882
|
+
refresh.has('identity') || refresh.has('versions') || refresh.has('verification');
|
|
883
|
+
if (refreshDeviceInfo && !initializedWithDeviceInfo) {
|
|
884
|
+
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
885
|
+
commands: this.commands,
|
|
886
|
+
request: getProtocolV2DeviceInfoRequest(),
|
|
887
|
+
});
|
|
888
|
+
this.updateState(
|
|
889
|
+
mapProtocolV2DeviceInfoToState(deviceInfo, this.state?.status.mode),
|
|
890
|
+
'device-info'
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if (refresh.has('status') && this.state?.status.mode === 'normal') {
|
|
895
|
+
const status = await requestProtocolV2DeviceStatus({ commands: this.commands });
|
|
896
|
+
this.updateState(mapProtocolV2DeviceStatusToState(status), 'device-status');
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
if (refresh.has('settings') && this.state?.status.mode === 'normal') {
|
|
900
|
+
const { message } = await this.commands.typedCall(
|
|
901
|
+
'DeviceSettingsGet',
|
|
902
|
+
'DeviceSettings',
|
|
903
|
+
{}
|
|
904
|
+
);
|
|
905
|
+
this.updateState(mapDeviceSettingsToState(message), 'apply-settings');
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
if (!this.state) {
|
|
910
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceInitializeFailed);
|
|
911
|
+
}
|
|
912
|
+
return params.includeRaw ? structuredClone(this.state) : createPublicDeviceState(this.state);
|
|
913
|
+
}
|
|
914
|
+
|
|
833
915
|
_updateFeatures(protoFeatures: PROTO.Features | Features, initSession?: boolean) {
|
|
834
916
|
const previousCacheDeviceKey = this.getSessionCacheDeviceKey();
|
|
835
917
|
let feat =
|
|
@@ -845,7 +927,7 @@ export class Device extends EventEmitter {
|
|
|
845
927
|
|
|
846
928
|
feat = fixFeaturesFirmwareVersion(feat);
|
|
847
929
|
|
|
848
|
-
this.
|
|
930
|
+
this.updateState(mapFeaturesToState(feat), 'initialize');
|
|
849
931
|
const nextCacheDeviceKey = this.getSessionCacheDeviceKey();
|
|
850
932
|
if (previousCacheDeviceKey && nextCacheDeviceKey) {
|
|
851
933
|
deviceWalletSessionStore.migrateDeviceKey(previousCacheDeviceKey, nextCacheDeviceKey);
|
|
@@ -853,42 +935,131 @@ export class Device extends EventEmitter {
|
|
|
853
935
|
if (feat.deviceId && feat.sessionId) {
|
|
854
936
|
this.setInternalState(feat.sessionId, initSession);
|
|
855
937
|
}
|
|
856
|
-
|
|
857
|
-
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
updateState(patch: DeviceStatePatch, source: DeviceStateUpdateSource) {
|
|
941
|
+
const result = this.stateStore.update(patch, source);
|
|
942
|
+
if (result.changedKeys.length === 0) return result.state;
|
|
943
|
+
|
|
944
|
+
const event: DeviceStateEvent = {
|
|
945
|
+
connectId: this.getConnectId() ?? null,
|
|
946
|
+
state: createPublicDeviceState(result.state),
|
|
947
|
+
revision: result.revision,
|
|
948
|
+
source,
|
|
949
|
+
changedKeys: result.changedKeys,
|
|
950
|
+
};
|
|
951
|
+
Log.debug('Device state patch committed', {
|
|
952
|
+
source,
|
|
953
|
+
keys: result.changedKeys,
|
|
954
|
+
});
|
|
955
|
+
this.emit(DEVICE.STATE, this, event);
|
|
956
|
+
if (result.state.protocol === 'V1') {
|
|
957
|
+
this.emit(DEVICE.FEATURES, this, projectFeatures(result.state));
|
|
958
|
+
}
|
|
959
|
+
return result.state;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
clearCachedSession(deviceId?: string, passphraseState?: string) {
|
|
963
|
+
const current = this.state;
|
|
964
|
+
if (!current?.session) return false;
|
|
965
|
+
if (deviceId && current.identity.deviceId !== deviceId) return false;
|
|
966
|
+
if (passphraseState && current.session.passphraseState !== passphraseState) return false;
|
|
967
|
+
return this.stateStore.clearSession('session-clear') !== undefined;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
updateFeaturesPatch(patch: Partial<Features>, source: DeviceStateUpdateSource) {
|
|
971
|
+
const currentFeatures = this.features;
|
|
972
|
+
if (!currentFeatures) return undefined;
|
|
973
|
+
|
|
974
|
+
const features = mergeDeviceFeaturesPatch(currentFeatures, patch);
|
|
975
|
+
if (features === currentFeatures) return currentFeatures;
|
|
976
|
+
|
|
977
|
+
const normalized = fixFeaturesFirmwareVersion(features);
|
|
978
|
+
this.updateState(mapFeaturesToState(normalized), source);
|
|
979
|
+
return this.features;
|
|
858
980
|
}
|
|
859
981
|
|
|
860
982
|
updateProtocolV2Features(deviceInfo?: ProtocolV2DeviceInfo, deviceStatus?: DeviceStatus | null) {
|
|
861
983
|
const previousCacheDeviceKey = this.getSessionCacheDeviceKey();
|
|
862
|
-
|
|
863
|
-
deviceStatus
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
})
|
|
872
|
-
);
|
|
873
|
-
this.features = features;
|
|
984
|
+
if (deviceStatus) {
|
|
985
|
+
this.updateState(mapProtocolV2DeviceStatusToState(deviceStatus), 'device-status');
|
|
986
|
+
}
|
|
987
|
+
if (deviceInfo) {
|
|
988
|
+
this.updateState(
|
|
989
|
+
mapProtocolV2DeviceInfoToState(deviceInfo, this.state?.status.mode),
|
|
990
|
+
'device-info'
|
|
991
|
+
);
|
|
992
|
+
}
|
|
874
993
|
const nextCacheDeviceKey = this.getSessionCacheDeviceKey();
|
|
875
994
|
if (previousCacheDeviceKey && nextCacheDeviceKey) {
|
|
876
995
|
deviceWalletSessionStore.migrateDeviceKey(previousCacheDeviceKey, nextCacheDeviceKey);
|
|
877
996
|
}
|
|
878
|
-
this.
|
|
879
|
-
this.emit(DEVICE.FEATURES, this, features);
|
|
880
|
-
return features;
|
|
997
|
+
return this.features as Features;
|
|
881
998
|
}
|
|
882
999
|
|
|
883
1000
|
updateProtocolV2Status(status: DeviceStatus) {
|
|
884
|
-
const previousDeviceInfo = this.
|
|
885
|
-
const previousStatus = this.
|
|
1001
|
+
const previousDeviceInfo = this.state?.raw?.protocolV2DeviceInfo;
|
|
1002
|
+
const previousStatus = this.state?.raw?.protocolV2DeviceStatus;
|
|
886
1003
|
return this.updateProtocolV2Features(previousDeviceInfo, {
|
|
887
1004
|
...previousStatus,
|
|
888
1005
|
...status,
|
|
889
1006
|
});
|
|
890
1007
|
}
|
|
891
1008
|
|
|
1009
|
+
markTransportDisconnected() {
|
|
1010
|
+
if (!this.isProtocolV2()) return;
|
|
1011
|
+
this.protocolV2StateNeedsReload = true;
|
|
1012
|
+
this.clearPreInitialized();
|
|
1013
|
+
this.clearCachedSession();
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
markProtocolV2Reboot(rebootType: DeviceRebootType) {
|
|
1017
|
+
if (!this.isProtocolV2()) return;
|
|
1018
|
+
|
|
1019
|
+
this.protocolV2StateNeedsReload = true;
|
|
1020
|
+
this.clearPreInitialized();
|
|
1021
|
+
let loaderMode: 'bootloader' | 'romloader' | undefined;
|
|
1022
|
+
if (rebootType === DeviceRebootType.Bootloader) {
|
|
1023
|
+
loaderMode = 'bootloader';
|
|
1024
|
+
} else if (rebootType === DeviceRebootType.Romloader) {
|
|
1025
|
+
loaderMode = 'romloader';
|
|
1026
|
+
}
|
|
1027
|
+
if (loaderMode) {
|
|
1028
|
+
this.updateState(
|
|
1029
|
+
{
|
|
1030
|
+
identity: { deviceId: null },
|
|
1031
|
+
status: {
|
|
1032
|
+
mode: loaderMode,
|
|
1033
|
+
initialized: null,
|
|
1034
|
+
unlocked: null,
|
|
1035
|
+
firmwarePresent: null,
|
|
1036
|
+
backupRequired: null,
|
|
1037
|
+
noBackup: null,
|
|
1038
|
+
unfinishedBackup: null,
|
|
1039
|
+
recoveryMode: null,
|
|
1040
|
+
passphraseProtection: null,
|
|
1041
|
+
pinProtection: null,
|
|
1042
|
+
attachToPinEnabled: null,
|
|
1043
|
+
unlockedAttachPin: null,
|
|
1044
|
+
},
|
|
1045
|
+
session: null,
|
|
1046
|
+
raw: { protocolV2DeviceStatus: null },
|
|
1047
|
+
},
|
|
1048
|
+
'transport-reconnect'
|
|
1049
|
+
);
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
this.updateState(
|
|
1054
|
+
{
|
|
1055
|
+
status: { mode: 'normal', unlocked: null },
|
|
1056
|
+
session: null,
|
|
1057
|
+
raw: { protocolV2DeviceStatus: null },
|
|
1058
|
+
},
|
|
1059
|
+
'transport-reconnect'
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
892
1063
|
/**
|
|
893
1064
|
* 暂时只在 acquire 后更新 Session ID
|
|
894
1065
|
* 后续看是否有需要依据 listen 返回结果更新
|
|
@@ -955,6 +1126,7 @@ export class Device extends EventEmitter {
|
|
|
955
1126
|
}
|
|
956
1127
|
}
|
|
957
1128
|
} catch (error) {
|
|
1129
|
+
await this.release();
|
|
958
1130
|
this.runPromise = null;
|
|
959
1131
|
if (error instanceof HardwareError) {
|
|
960
1132
|
return Promise.reject(error);
|
|
@@ -1055,22 +1227,23 @@ export class Device extends EventEmitter {
|
|
|
1055
1227
|
}
|
|
1056
1228
|
|
|
1057
1229
|
getMode() {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1230
|
+
switch (this.state?.status.mode) {
|
|
1231
|
+
case 'bootloader':
|
|
1232
|
+
case 'romloader':
|
|
1233
|
+
return EOneKeyDeviceMode.bootloader;
|
|
1234
|
+
case 'notInitialized':
|
|
1235
|
+
return EOneKeyDeviceMode.notInitialized;
|
|
1236
|
+
case 'backupMode':
|
|
1237
|
+
return EOneKeyDeviceMode.backupMode;
|
|
1238
|
+
case 'normal':
|
|
1239
|
+
return EOneKeyDeviceMode.normal;
|
|
1240
|
+
default:
|
|
1241
|
+
break;
|
|
1066
1242
|
}
|
|
1067
1243
|
|
|
1068
|
-
if (this.features?.
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
// normal mode
|
|
1244
|
+
if (this.features?.bootloaderMode) return EOneKeyDeviceMode.bootloader;
|
|
1245
|
+
if (this.features?.initialized === false) return EOneKeyDeviceMode.notInitialized;
|
|
1246
|
+
if (this.features?.noBackup) return EOneKeyDeviceMode.backupMode;
|
|
1074
1247
|
return EOneKeyDeviceMode.normal;
|
|
1075
1248
|
}
|
|
1076
1249
|
|
|
@@ -1165,6 +1338,7 @@ export class Device extends EventEmitter {
|
|
|
1165
1338
|
|
|
1166
1339
|
async lockDevice(): Promise<Success> {
|
|
1167
1340
|
const res = await this.commands.typedCall('LockDevice', 'Success', {});
|
|
1341
|
+
this.updateState({ status: { unlocked: false } }, 'lock');
|
|
1168
1342
|
return res.message;
|
|
1169
1343
|
}
|
|
1170
1344
|
|
|
@@ -1194,12 +1368,7 @@ export class Device extends EventEmitter {
|
|
|
1194
1368
|
throw error;
|
|
1195
1369
|
}
|
|
1196
1370
|
|
|
1197
|
-
|
|
1198
|
-
'DeviceStatusGet',
|
|
1199
|
-
'DeviceStatus',
|
|
1200
|
-
{}
|
|
1201
|
-
);
|
|
1202
|
-
return this.updateProtocolV2Status(status);
|
|
1371
|
+
return this.updateFeaturesPatch({ unlocked: true }, 'unlock');
|
|
1203
1372
|
}
|
|
1204
1373
|
|
|
1205
1374
|
const firmwareVersion = this.getCurrentFirmwareVersionString() ?? '0.0.0';
|
|
@@ -1220,11 +1389,20 @@ export class Device extends EventEmitter {
|
|
|
1220
1389
|
if (supportUnlock) {
|
|
1221
1390
|
const res = await this.commands.typedCall('UnLockDevice', 'UnLockDeviceResponse');
|
|
1222
1391
|
if (this.features) {
|
|
1223
|
-
this.
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1392
|
+
this.updateState(
|
|
1393
|
+
{
|
|
1394
|
+
status: {
|
|
1395
|
+
unlocked: res.message.unlocked == null ? null : res.message.unlocked,
|
|
1396
|
+
unlockedAttachPin:
|
|
1397
|
+
res.message.unlocked_attach_pin == null ? null : res.message.unlocked_attach_pin,
|
|
1398
|
+
passphraseProtection:
|
|
1399
|
+
res.message.passphrase_protection == null
|
|
1400
|
+
? null
|
|
1401
|
+
: res.message.passphrase_protection,
|
|
1402
|
+
},
|
|
1403
|
+
},
|
|
1404
|
+
'unlock'
|
|
1405
|
+
);
|
|
1228
1406
|
|
|
1229
1407
|
return Promise.resolve(this.features);
|
|
1230
1408
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Features } from '../types';
|
|
2
|
+
|
|
3
|
+
export type DeviceFeaturesUpdateSource =
|
|
4
|
+
| 'device-info'
|
|
5
|
+
| 'device-status'
|
|
6
|
+
| 'device-settings-get'
|
|
7
|
+
| 'device-settings-set'
|
|
8
|
+
| 'apply-settings'
|
|
9
|
+
| 'unlock'
|
|
10
|
+
| 'cache';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 合并设备状态的字段级 patch。
|
|
14
|
+
*
|
|
15
|
+
* `undefined` 表示本次响应未提供该字段,不覆盖缓存;`null` 表示设备明确
|
|
16
|
+
* 返回空值,需要写入缓存。raw 等对象字段由调用方构造完整值后再传入,
|
|
17
|
+
* 这里不做隐式深合并。
|
|
18
|
+
*/
|
|
19
|
+
export function mergeDeviceFeaturesPatch(previous: Features, patch: Partial<Features>): Features {
|
|
20
|
+
let next = previous;
|
|
21
|
+
|
|
22
|
+
for (const [key, value] of Object.entries(patch) as Array<
|
|
23
|
+
[keyof Features, Features[keyof Features] | undefined]
|
|
24
|
+
>) {
|
|
25
|
+
if (value !== undefined && !Object.is(previous[key], value)) {
|
|
26
|
+
if (next === previous) {
|
|
27
|
+
next = { ...previous };
|
|
28
|
+
}
|
|
29
|
+
(next as Record<keyof Features, Features[keyof Features]>)[key] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return next;
|
|
34
|
+
}
|
package/src/device/DevicePool.ts
CHANGED
|
@@ -106,6 +106,7 @@ export class DevicePool extends EventEmitter {
|
|
|
106
106
|
if (exist) {
|
|
107
107
|
// Log.debug('find existed Device: ', connectId);
|
|
108
108
|
device.updateDescriptor(exist, true);
|
|
109
|
+
await this._refreshRuntimeState(device, initOptions);
|
|
109
110
|
devices[connectId] = device;
|
|
110
111
|
deviceList.push(device);
|
|
111
112
|
await this._checkDevicePool(initOptions, connectId);
|
|
@@ -156,12 +157,36 @@ export class DevicePool extends EventEmitter {
|
|
|
156
157
|
device = Device.fromDescriptor(descriptor);
|
|
157
158
|
device.deviceConnector = this.connector;
|
|
158
159
|
await device.connect(initOptions?.connectProtocol);
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
try {
|
|
161
|
+
await device.initialize(initOptions);
|
|
162
|
+
if (initOptions?.refreshRuntimeState && device.isProtocolV2()) {
|
|
163
|
+
await device.getDeviceState({ refreshSections: ['status'] });
|
|
164
|
+
}
|
|
165
|
+
} finally {
|
|
166
|
+
await device.release();
|
|
167
|
+
}
|
|
161
168
|
}
|
|
162
169
|
return device;
|
|
163
170
|
}
|
|
164
171
|
|
|
172
|
+
static async _refreshRuntimeState(device: Device, initOptions?: InitOptions) {
|
|
173
|
+
if (!initOptions?.refreshRuntimeState || !device.isProtocolV2()) return;
|
|
174
|
+
let refreshError: unknown;
|
|
175
|
+
await device.run(
|
|
176
|
+
async () => {
|
|
177
|
+
try {
|
|
178
|
+
await device.getDeviceState({ refreshSections: ['status'] });
|
|
179
|
+
} catch (error) {
|
|
180
|
+
// Device.run 在回调正常结束后统一 release;随后再把真实读取错误交给调用方。
|
|
181
|
+
refreshError = error;
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{ connectProtocol: initOptions.connectProtocol }
|
|
185
|
+
);
|
|
186
|
+
if (refreshError instanceof Error) throw refreshError;
|
|
187
|
+
if (refreshError) throw new Error(String(refreshError));
|
|
188
|
+
}
|
|
189
|
+
|
|
165
190
|
static async _checkDevicePool(initOptions?: InitOptions, connectId?: string) {
|
|
166
191
|
await this._sendConnectMessage(initOptions, connectId);
|
|
167
192
|
this._sendDisconnectMessage();
|
|
@@ -170,21 +195,21 @@ export class DevicePool extends EventEmitter {
|
|
|
170
195
|
static async _sendConnectMessage(initOptions?: InitOptions, connectId?: string) {
|
|
171
196
|
for (let i = this.connectedPool.length - 1; i >= 0; i--) {
|
|
172
197
|
const descriptor = this.connectedPool[i];
|
|
173
|
-
if (connectId
|
|
174
|
-
|
|
198
|
+
if (!connectId || descriptor.path === connectId) {
|
|
199
|
+
const device = await this._createDevice(descriptor, initOptions);
|
|
200
|
+
Log.debug('emit DEVICE.CONNECT: ', device?.features);
|
|
201
|
+
this.emitter.emit(DEVICE.CONNECT, device);
|
|
202
|
+
this.connectedPool.splice(i, 1);
|
|
175
203
|
}
|
|
176
|
-
const device = await this._createDevice(descriptor, initOptions);
|
|
177
|
-
Log.debug('emit DEVICE.CONNECT: ', device?.features);
|
|
178
|
-
this.emitter.emit(DEVICE.CONNECT, device);
|
|
179
|
-
this.connectedPool.splice(i, 1);
|
|
180
204
|
}
|
|
181
205
|
}
|
|
182
206
|
|
|
183
207
|
static _sendDisconnectMessage() {
|
|
184
208
|
for (let i = this.disconnectPool.length - 1; i >= 0; i--) {
|
|
185
|
-
const descriptor = this.
|
|
209
|
+
const descriptor = this.disconnectPool[i];
|
|
186
210
|
const device = descriptor?.path ? this.getDeviceByPath(descriptor.path) : null;
|
|
187
211
|
if (device) {
|
|
212
|
+
device.markTransportDisconnected();
|
|
188
213
|
this.emitter.emit(DEVICE.DISCONNECT, device);
|
|
189
214
|
}
|
|
190
215
|
this.disconnectPool.splice(i, 1);
|
|
@@ -224,6 +249,7 @@ export class DevicePool extends EventEmitter {
|
|
|
224
249
|
}
|
|
225
250
|
|
|
226
251
|
Log.debug('emit DEVICE.DISCONNECT: ', device.features);
|
|
252
|
+
device.markTransportDisconnected();
|
|
227
253
|
this.emitter.emit(DEVICE.DISCONNECT, device);
|
|
228
254
|
});
|
|
229
255
|
}
|