@onekeyfe/hd-core 1.2.0-alpha.34 → 1.2.0-alpha.35
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__/DeviceCommands.test.ts +84 -0
- package/__tests__/device-settings.test.ts +3 -0
- package/__tests__/device-wallet-session-store.test.ts +80 -7
- package/__tests__/evmSignTransaction.test.ts +69 -0
- package/__tests__/get-device-state.test.ts +78 -30
- package/__tests__/open-wallet-session.test.ts +186 -51
- package/__tests__/protocol-v2.test.ts +490 -112
- package/dist/api/FirmwareUpdateV4.d.ts +1 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
- package/dist/api/evm/protocol.d.ts +3 -0
- package/dist/api/evm/protocol.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +1 -1
- package/dist/device/Device.d.ts +3 -2
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DeviceWalletSessionStore.d.ts +0 -1
- package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.js +606 -641
- package/dist/protocols/protocol-v2/features.d.ts +17 -6
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +2 -2
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +1 -0
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +1 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +1 -1
- package/dist/types/api/openWalletSession.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +2 -2
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +2 -1
- package/dist/types/device.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/FirmwareUpdateV4.ts +26 -8
- package/src/api/GetPassphraseState.ts +11 -0
- package/src/api/OpenWalletSession.ts +15 -7
- package/src/api/evm/EVMGetAddress.ts +2 -2
- package/src/api/evm/EVMGetPublicKey.ts +2 -2
- package/src/api/evm/EVMSignMessage.ts +2 -2
- package/src/api/evm/EVMSignTransaction.ts +2 -2
- package/src/api/evm/EVMSignTypedData.ts +35 -45
- package/src/api/evm/EVMVerifyMessage.ts +2 -2
- package/src/api/evm/protocol.ts +6 -0
- package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +2 -2
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +1 -1
- package/src/data/messages/messages-protocol-v2.json +393 -485
- package/src/device/Device.ts +63 -37
- package/src/device/DeviceCommands.ts +12 -2
- package/src/device/DeviceWalletSessionStore.ts +0 -14
- package/src/deviceProfile/buildDeviceFeatures.ts +1 -1
- package/src/protocols/protocol-v2/features.ts +58 -16
- package/src/protocols/protocol-v2/index.ts +5 -0
- package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +8 -3
- package/src/protocols/protocol-v2/unlockRetry.ts +18 -7
- package/src/protocols/protocol-v2/walletSession.ts +35 -35
- package/src/types/api/openWalletSession.ts +1 -1
- package/src/types/api/protocolV2.ts +2 -2
- package/src/types/device.ts +2 -0
package/src/device/Device.ts
CHANGED
|
@@ -55,6 +55,7 @@ import { DataManager } from '../data-manager';
|
|
|
55
55
|
import TransportManager from '../data-manager/TransportManager';
|
|
56
56
|
import { toHardened } from '../api/helpers/pathUtils';
|
|
57
57
|
import {
|
|
58
|
+
PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE,
|
|
58
59
|
PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST,
|
|
59
60
|
PROTOCOL_V2_FULL_DEVICE_INFO_REQUEST,
|
|
60
61
|
PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
@@ -62,8 +63,11 @@ import {
|
|
|
62
63
|
getProtocolV2RuntimeMode,
|
|
63
64
|
requestProtocolV2DeviceInfo,
|
|
64
65
|
requestProtocolV2DeviceStatus,
|
|
66
|
+
requestProtocolV2ProtocolInfo,
|
|
67
|
+
supportsProtocolV2Message,
|
|
65
68
|
} from '../protocols/protocol-v2/features';
|
|
66
69
|
import { buildProtocolV1FeaturesPayload } from '../deviceProfile';
|
|
70
|
+
import { resolveProtocolV2DeviceIdentity } from '../deviceProfile/protocolV2DeviceIdentity';
|
|
67
71
|
|
|
68
72
|
import type { PROTO } from '../constants';
|
|
69
73
|
import type { DeviceStateReadOptions } from '../types/api/getDeviceState';
|
|
@@ -77,6 +81,7 @@ import type { Deferred, HardwareConnectProtocol } from '@onekeyfe/hd-shared';
|
|
|
77
81
|
import type {
|
|
78
82
|
OneKeyDeviceInfo as DeviceDescriptor,
|
|
79
83
|
DeviceStatus,
|
|
84
|
+
ProtocolInfo,
|
|
80
85
|
ProtocolV2DeviceInfo,
|
|
81
86
|
Success,
|
|
82
87
|
} from '@onekeyfe/hd-transport';
|
|
@@ -835,8 +840,8 @@ export class Device extends EventEmitter {
|
|
|
835
840
|
|
|
836
841
|
const expectedDeviceId = options?.deviceId;
|
|
837
842
|
if (expectedDeviceId) {
|
|
838
|
-
//
|
|
839
|
-
//
|
|
843
|
+
// 先只读校验物理设备身份;钱包上下文仍由下方携带完整参数的
|
|
844
|
+
// Initialize 选择,避免标准钱包请求复用此前的隐藏钱包上下文。
|
|
840
845
|
this.passphraseState = undefined;
|
|
841
846
|
const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
|
|
842
847
|
this._updateFeatures(message);
|
|
@@ -864,15 +869,7 @@ export class Device extends EventEmitter {
|
|
|
864
869
|
payload.derive_cardano = true;
|
|
865
870
|
}
|
|
866
871
|
|
|
867
|
-
|
|
868
|
-
!expectedDeviceId ||
|
|
869
|
-
Boolean(internalState) ||
|
|
870
|
-
Boolean(options?.passphraseState) ||
|
|
871
|
-
options?.deriveCardano === true ||
|
|
872
|
-
options?.initSession === true;
|
|
873
|
-
if (requiresWalletInitialize) {
|
|
874
|
-
await callInitialize(payload, options?.initSession);
|
|
875
|
-
}
|
|
872
|
+
await callInitialize(payload, options?.initSession);
|
|
876
873
|
} catch (error) {
|
|
877
874
|
Log.error('Initialization failed:', error);
|
|
878
875
|
throw error;
|
|
@@ -1048,40 +1045,60 @@ export class Device extends EventEmitter {
|
|
|
1048
1045
|
}
|
|
1049
1046
|
|
|
1050
1047
|
async probeProtocolV2RuntimeState(deviceInfo: ProtocolV2DeviceInfo, timeoutMs?: number) {
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1048
|
+
const protocolInfo = await requestProtocolV2ProtocolInfo({
|
|
1049
|
+
commands: this.commands,
|
|
1050
|
+
timeoutMs,
|
|
1051
|
+
});
|
|
1052
|
+
const runtimeMode = getProtocolV2RuntimeMode(protocolInfo);
|
|
1053
|
+
const protocolV2DeviceType = resolveProtocolV2DeviceIdentity(
|
|
1054
|
+
deviceInfo.hw?.Device_type
|
|
1055
|
+
).deviceType;
|
|
1056
|
+
if (runtimeMode === 'romloader' && protocolV2DeviceType !== EDeviceType.Pro2) {
|
|
1057
|
+
throw ERRORS.TypedError(
|
|
1058
|
+
HardwareErrorCode.DeviceInitializeFailed,
|
|
1059
|
+
'Protocol V2 romloader mode is only supported for Pro2.'
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
const deviceStatusSupported = supportsProtocolV2Message(
|
|
1063
|
+
protocolInfo,
|
|
1064
|
+
PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE
|
|
1065
|
+
);
|
|
1066
|
+
|
|
1067
|
+
if (runtimeMode === 'bootloader' || runtimeMode === 'romloader') {
|
|
1068
|
+
return this.updateProtocolV2Features(deviceInfo, null, runtimeMode, protocolInfo);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if (!deviceStatusSupported) {
|
|
1072
|
+
if (runtimeMode === 'normal') {
|
|
1073
|
+
return this.updateProtocolV2Features(deviceInfo, null, runtimeMode, protocolInfo);
|
|
1074
|
+
}
|
|
1075
|
+
throw ERRORS.TypedError(
|
|
1076
|
+
HardwareErrorCode.DeviceInitializeFailed,
|
|
1077
|
+
`Unknown Protocol V2 build fingerprint without DeviceStatusGet capability: ${protocolInfo.build_fingerprint}`
|
|
1078
|
+
);
|
|
1067
1079
|
}
|
|
1068
|
-
|
|
1080
|
+
|
|
1081
|
+
const deviceStatus = await requestProtocolV2DeviceStatus({
|
|
1082
|
+
commands: this.commands,
|
|
1083
|
+
timeoutMs,
|
|
1084
|
+
});
|
|
1085
|
+
return this.updateProtocolV2Features(deviceInfo, deviceStatus, 'normal', protocolInfo);
|
|
1069
1086
|
}
|
|
1070
1087
|
|
|
1071
1088
|
updateProtocolV2Features(
|
|
1072
1089
|
deviceInfo?: ProtocolV2DeviceInfo,
|
|
1073
1090
|
deviceStatus?: DeviceStatus | null,
|
|
1074
|
-
runtimeMode?: ProtocolV2RuntimeMode
|
|
1091
|
+
runtimeMode?: ProtocolV2RuntimeMode,
|
|
1092
|
+
protocolInfo?: ProtocolInfo
|
|
1075
1093
|
) {
|
|
1076
1094
|
const previousDeviceId = this.getCurrentDeviceId();
|
|
1077
|
-
const resolvedMode =
|
|
1078
|
-
runtimeMode ??
|
|
1079
|
-
(deviceStatus
|
|
1080
|
-
? getProtocolV2RuntimeMode({ deviceInfo, deviceStatusAvailable: true })
|
|
1081
|
-
: this.state?.status.mode);
|
|
1095
|
+
const resolvedMode = runtimeMode ?? (deviceStatus ? 'normal' : this.state?.status.mode);
|
|
1082
1096
|
if (deviceInfo) {
|
|
1083
1097
|
this.updateState(mapProtocolV2DeviceInfoToState(deviceInfo, resolvedMode), 'device-info');
|
|
1084
1098
|
}
|
|
1099
|
+
if (protocolInfo) {
|
|
1100
|
+
this.updateState({ raw: { protocolV2ProtocolInfo: protocolInfo } }, 'device-info');
|
|
1101
|
+
}
|
|
1085
1102
|
if (deviceStatus) {
|
|
1086
1103
|
this.updateState(mapProtocolV2DeviceStatusToState(deviceStatus), 'device-status');
|
|
1087
1104
|
}
|
|
@@ -1150,7 +1167,7 @@ export class Device extends EventEmitter {
|
|
|
1150
1167
|
attachToPinEnabled: null,
|
|
1151
1168
|
unlockedAttachPin: null,
|
|
1152
1169
|
},
|
|
1153
|
-
raw: { protocolV2DeviceStatus: null },
|
|
1170
|
+
raw: { protocolV2ProtocolInfo: null, protocolV2DeviceStatus: null },
|
|
1154
1171
|
},
|
|
1155
1172
|
'transport-reconnect'
|
|
1156
1173
|
);
|
|
@@ -1160,7 +1177,7 @@ export class Device extends EventEmitter {
|
|
|
1160
1177
|
this.updateState(
|
|
1161
1178
|
{
|
|
1162
1179
|
status: { mode: 'normal', unlocked: null },
|
|
1163
|
-
raw: { protocolV2DeviceStatus: null },
|
|
1180
|
+
raw: { protocolV2ProtocolInfo: null, protocolV2DeviceStatus: null },
|
|
1164
1181
|
},
|
|
1165
1182
|
'transport-reconnect'
|
|
1166
1183
|
);
|
|
@@ -1406,7 +1423,16 @@ export class Device extends EventEmitter {
|
|
|
1406
1423
|
|
|
1407
1424
|
isBootloader() {
|
|
1408
1425
|
if (!this.state) return undefined;
|
|
1409
|
-
return this.state.status.mode === 'bootloader'
|
|
1426
|
+
return this.state.status.mode === 'bootloader';
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
isRomloader() {
|
|
1430
|
+
if (!this.state) return undefined;
|
|
1431
|
+
return (
|
|
1432
|
+
this.isProtocolV2() &&
|
|
1433
|
+
this.getCurrentDeviceType() === EDeviceType.Pro2 &&
|
|
1434
|
+
this.state.status.mode === 'romloader'
|
|
1435
|
+
);
|
|
1410
1436
|
}
|
|
1411
1437
|
|
|
1412
1438
|
isInitialized() {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type Messages,
|
|
7
7
|
type Transport,
|
|
8
8
|
type TransportCallOptions,
|
|
9
|
+
getSafeTransportLogPayload,
|
|
9
10
|
} from '@onekeyfe/hd-transport';
|
|
10
11
|
|
|
11
12
|
import TransportManager from '../data-manager/TransportManager';
|
|
@@ -282,7 +283,11 @@ export class DeviceCommands {
|
|
|
282
283
|
): Promise<DefaultMessageResponse> {
|
|
283
284
|
const shouldReduceDebug = shouldReduceDebugForCall(type);
|
|
284
285
|
if (!shouldReduceDebug) {
|
|
285
|
-
Log.debug(
|
|
286
|
+
Log.debug(
|
|
287
|
+
'[DeviceCommands] [call] Sending',
|
|
288
|
+
type,
|
|
289
|
+
getSafeTransportLogPayload(msg ?? {}, type)
|
|
290
|
+
);
|
|
286
291
|
}
|
|
287
292
|
|
|
288
293
|
try {
|
|
@@ -290,13 +295,18 @@ export class DeviceCommands {
|
|
|
290
295
|
this.callPromise = promise;
|
|
291
296
|
const res = await promise;
|
|
292
297
|
if (!shouldReduceDebug) {
|
|
293
|
-
LogCore.debug(
|
|
298
|
+
LogCore.debug(
|
|
299
|
+
'[DeviceCommands] [call] Received',
|
|
300
|
+
res.type,
|
|
301
|
+
getSafeTransportLogPayload(res.message, res.type)
|
|
302
|
+
);
|
|
294
303
|
}
|
|
295
304
|
return res;
|
|
296
305
|
} catch (error) {
|
|
297
306
|
LogCore.debug('[DeviceCommands] [call] Received error', {
|
|
298
307
|
request: type,
|
|
299
308
|
errorCode: error?.errorCode,
|
|
309
|
+
response: getSafeTransportLogPayload(error?.response?.data, type),
|
|
300
310
|
});
|
|
301
311
|
if (error.errorCode === HardwareErrorCode.BleDeviceBondError) {
|
|
302
312
|
return {
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
export class DeviceWalletSessionStore {
|
|
2
|
-
private static readonly MAX_SESSIONS_PER_DEVICE = 3;
|
|
3
|
-
|
|
4
2
|
private readonly walletSessions = new Map<string, Map<string, string>>();
|
|
5
3
|
|
|
6
4
|
private readonly standardWalletSessions = new Map<
|
|
@@ -22,18 +20,6 @@ export class DeviceWalletSessionStore {
|
|
|
22
20
|
deviceSessions = new Map<string, string>();
|
|
23
21
|
this.walletSessions.set(deviceKey, deviceSessions);
|
|
24
22
|
}
|
|
25
|
-
if (
|
|
26
|
-
!deviceSessions.has(passphraseState) &&
|
|
27
|
-
deviceSessions.size >= DeviceWalletSessionStore.MAX_SESSIONS_PER_DEVICE
|
|
28
|
-
) {
|
|
29
|
-
const oldestPassphraseState = deviceSessions.keys().next().value as string | undefined;
|
|
30
|
-
if (oldestPassphraseState) {
|
|
31
|
-
deviceSessions.delete(oldestPassphraseState);
|
|
32
|
-
if (this.standardWalletSessions.get(deviceKey)?.passphraseState === oldestPassphraseState) {
|
|
33
|
-
this.standardWalletSessions.delete(deviceKey);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
23
|
deviceSessions.set(passphraseState, sessionId);
|
|
38
24
|
}
|
|
39
25
|
|
|
@@ -260,7 +260,7 @@ export const buildProtocolV2FeaturesPayload = ({
|
|
|
260
260
|
const attachToPinEnabled = status?.attach_to_pin_enabled ?? cached?.attachToPinEnabled ?? null;
|
|
261
261
|
const unlockedAttachPin =
|
|
262
262
|
status?.unlocked_by_attach_to_pin ?? cached?.unlockedAttachPin ?? undefined;
|
|
263
|
-
const bootloaderMode = runtimeMode === 'bootloader';
|
|
263
|
+
const bootloaderMode = runtimeMode === 'bootloader' || runtimeMode === 'romloader';
|
|
264
264
|
let mode: DeviceFeaturesMode = 'unknown';
|
|
265
265
|
if (runtimeMode) {
|
|
266
266
|
mode = runtimeMode;
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
DeviceInfoGet,
|
|
5
5
|
DeviceSEInfo,
|
|
6
6
|
DeviceStatus,
|
|
7
|
+
ProtocolInfo,
|
|
7
8
|
ProtocolV2DeviceInfo,
|
|
8
9
|
} from '@onekeyfe/hd-transport';
|
|
9
10
|
import type { DeviceCommands } from '../../device/DeviceCommands';
|
|
@@ -53,27 +54,54 @@ export const getProtocolV2SeState = (se?: DeviceSEInfo): ProtocolV2SeStateLabel
|
|
|
53
54
|
export const getProtocolV2SeType = (se?: DeviceSEInfo): string | null =>
|
|
54
55
|
normalizeEnumValue(DeviceSeType, se?.type);
|
|
55
56
|
|
|
56
|
-
export type ProtocolV2RuntimeMode = 'normal' | 'bootloader';
|
|
57
|
+
export type ProtocolV2RuntimeMode = 'normal' | 'bootloader' | 'romloader';
|
|
57
58
|
|
|
58
59
|
/**
|
|
59
|
-
*
|
|
60
|
-
* binary
|
|
61
|
-
* is a separate recovery flow and is excluded until its contract is defined.
|
|
62
|
-
*
|
|
63
|
-
* The temporary contract has two states: DeviceStatusGet success means normal and
|
|
64
|
-
* failure means bootloader. Do not infer mode from application/SE/romloader fields.
|
|
60
|
+
* Protocol V2 fingerprints use:
|
|
61
|
+
* <binary>__<version>__<commit>__<PROD|DEV>__<DEBUG|RELEASE>
|
|
65
62
|
*/
|
|
66
|
-
export
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}): ProtocolV2RuntimeMode => {
|
|
73
|
-
if (deviceStatusAvailable) return 'normal';
|
|
74
|
-
return 'bootloader';
|
|
63
|
+
export type ProtocolV2BuildFingerprint = {
|
|
64
|
+
binary: 'application' | 'bootloader' | 'romloader';
|
|
65
|
+
version: string;
|
|
66
|
+
commit: string;
|
|
67
|
+
environment: 'PROD' | 'DEV';
|
|
68
|
+
buildType: 'DEBUG' | 'RELEASE';
|
|
75
69
|
};
|
|
76
70
|
|
|
71
|
+
export const parseProtocolV2BuildFingerprint = (
|
|
72
|
+
buildFingerprint: string | null | undefined
|
|
73
|
+
): ProtocolV2BuildFingerprint | null => {
|
|
74
|
+
if (!buildFingerprint) return null;
|
|
75
|
+
const [binary, version, commit, environment, buildType, ...extra] = buildFingerprint.split('__');
|
|
76
|
+
if (
|
|
77
|
+
extra.length > 0 ||
|
|
78
|
+
(binary !== 'application' && binary !== 'bootloader' && binary !== 'romloader') ||
|
|
79
|
+
!version ||
|
|
80
|
+
!commit ||
|
|
81
|
+
(environment !== 'PROD' && environment !== 'DEV') ||
|
|
82
|
+
(buildType !== 'DEBUG' && buildType !== 'RELEASE')
|
|
83
|
+
) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return { binary, version, commit, environment, buildType };
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const getProtocolV2RuntimeMode = (
|
|
90
|
+
protocolInfo: ProtocolInfo
|
|
91
|
+
): ProtocolV2RuntimeMode | undefined => {
|
|
92
|
+
const binary = parseProtocolV2BuildFingerprint(protocolInfo.build_fingerprint)?.binary;
|
|
93
|
+
if (binary === 'application') return 'normal';
|
|
94
|
+
return binary;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// MessageType_DeviceStatusGet in the Protocol V2 protobuf registry.
|
|
98
|
+
export const PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE = 60602;
|
|
99
|
+
|
|
100
|
+
export const supportsProtocolV2Message = (
|
|
101
|
+
protocolInfo: ProtocolInfo,
|
|
102
|
+
messageType: number
|
|
103
|
+
): boolean => protocolInfo.supported_messages.includes(messageType);
|
|
104
|
+
|
|
77
105
|
export const PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST = {
|
|
78
106
|
targets: {
|
|
79
107
|
hw: true,
|
|
@@ -131,6 +159,20 @@ export const PROTOCOL_V2_FULL_DEVICE_INFO_REQUEST = {
|
|
|
131
159
|
export const PROTOCOL_V2_DEVICE_INFO_REQUEST = PROTOCOL_V2_FULL_DEVICE_INFO_REQUEST;
|
|
132
160
|
export const PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS = 10 * 1000;
|
|
133
161
|
|
|
162
|
+
export async function requestProtocolV2ProtocolInfo({
|
|
163
|
+
commands,
|
|
164
|
+
timeoutMs,
|
|
165
|
+
}: {
|
|
166
|
+
commands: DeviceCommands;
|
|
167
|
+
timeoutMs?: number;
|
|
168
|
+
}): Promise<ProtocolInfo> {
|
|
169
|
+
const response =
|
|
170
|
+
timeoutMs === undefined
|
|
171
|
+
? await commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {})
|
|
172
|
+
: await commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {}, { timeoutMs });
|
|
173
|
+
return response.message;
|
|
174
|
+
}
|
|
175
|
+
|
|
134
176
|
export async function requestProtocolV2DeviceInfo({
|
|
135
177
|
commands,
|
|
136
178
|
timeoutMs = PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS,
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
export {
|
|
2
2
|
PROTOCOL_V2_DEVICE_INFO_REQUEST,
|
|
3
3
|
PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS,
|
|
4
|
+
PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE,
|
|
4
5
|
PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST,
|
|
5
6
|
PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
6
7
|
getProtocolV2RuntimeMode,
|
|
7
8
|
getProtocolV2SeState,
|
|
8
9
|
getProtocolV2SeType,
|
|
10
|
+
parseProtocolV2BuildFingerprint,
|
|
11
|
+
requestProtocolV2ProtocolInfo,
|
|
12
|
+
supportsProtocolV2Message,
|
|
9
13
|
} from './features';
|
|
10
14
|
export type {
|
|
15
|
+
ProtocolV2BuildFingerprint,
|
|
11
16
|
ProtocolV2DeviceInfo,
|
|
12
17
|
ProtocolV2FirmwareImageInfo,
|
|
13
18
|
ProtocolV2SEInfo,
|
|
@@ -2,14 +2,19 @@ import type { DeviceSettings, DeviceSettingsPage } from '@onekeyfe/hd-transport'
|
|
|
2
2
|
import type { UnlockPolicy } from '../../api/BaseMethod';
|
|
3
3
|
import type { ProtocolV2InteractionDescriptor } from './uiInteraction';
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
export const PROTOCOL_V2_LOCK_FREE_DEVICE_SETTINGS = [
|
|
6
6
|
'language',
|
|
7
7
|
'brightness',
|
|
8
|
+
'animation_enable',
|
|
9
|
+
'tap_to_wake',
|
|
8
10
|
'haptic_feedback',
|
|
9
|
-
|
|
11
|
+
'device_name_display_enabled',
|
|
12
|
+
] as const satisfies readonly (keyof DeviceSettings)[];
|
|
13
|
+
|
|
14
|
+
const lockFreeDeviceSettings = new Set<keyof DeviceSettings>(PROTOCOL_V2_LOCK_FREE_DEVICE_SETTINGS);
|
|
10
15
|
|
|
11
16
|
export const getProtocolV2SettingsUnlockPolicy = (settings: DeviceSettings): UnlockPolicy =>
|
|
12
|
-
Object.keys(settings).every(key =>
|
|
17
|
+
Object.keys(settings).every(key => lockFreeDeviceSettings.has(key as keyof DeviceSettings))
|
|
13
18
|
? 'none'
|
|
14
19
|
: 'unlock-before-run';
|
|
15
20
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LoggerNames, getLogger } from '../../utils';
|
|
2
2
|
import { isDeviceLockedError } from './lockedError';
|
|
3
3
|
import { isProtocolV2UiEnabled, resolveProtocolV2UiInteraction } from './uiInteraction';
|
|
4
|
-
import { restoreProtocolV2WalletSession } from './walletSession';
|
|
4
|
+
import { refreshProtocolV2DeviceStatus, restoreProtocolV2WalletSession } from './walletSession';
|
|
5
5
|
|
|
6
6
|
import type { BaseMethod } from '../../api/BaseMethod';
|
|
7
7
|
import type { Device } from '../../device/Device';
|
|
@@ -46,8 +46,23 @@ export async function runMethodWithUnlockRetry(
|
|
|
46
46
|
uiCoordinator?: UiInteractionCoordinator
|
|
47
47
|
) {
|
|
48
48
|
const shouldEmitUi = isProtocolV2UiEnabled(method);
|
|
49
|
+
const isProtocolV2 = device.isProtocolV2();
|
|
50
|
+
const requiresFreshStatus =
|
|
51
|
+
isProtocolV2 &&
|
|
52
|
+
method.unlockPolicy === 'unlock-before-run' &&
|
|
53
|
+
!device.isBootloader?.() &&
|
|
54
|
+
!device.isRomloader?.();
|
|
55
|
+
|
|
56
|
+
if (requiresFreshStatus) {
|
|
57
|
+
await refreshProtocolV2DeviceStatus(device);
|
|
58
|
+
}
|
|
59
|
+
|
|
49
60
|
const shouldUnlockBeforeRun =
|
|
50
|
-
|
|
61
|
+
isProtocolV2 &&
|
|
62
|
+
method.unlockPolicy !== 'none' &&
|
|
63
|
+
!device.isBootloader?.() &&
|
|
64
|
+
!device.isRomloader?.() &&
|
|
65
|
+
device.features?.unlocked === false;
|
|
51
66
|
|
|
52
67
|
if (shouldUnlockBeforeRun) {
|
|
53
68
|
if (shouldEmitUi) {
|
|
@@ -68,11 +83,7 @@ export async function runMethodWithUnlockRetry(
|
|
|
68
83
|
try {
|
|
69
84
|
return await method.run();
|
|
70
85
|
} catch (error) {
|
|
71
|
-
if (
|
|
72
|
-
!device.isProtocolV2() ||
|
|
73
|
-
method.unlockPolicy !== 'retry-on-locked' ||
|
|
74
|
-
!isDeviceLockedError(error)
|
|
75
|
-
) {
|
|
86
|
+
if (!isProtocolV2 || method.unlockPolicy !== 'retry-on-locked' || !isDeviceLockedError(error)) {
|
|
76
87
|
throw error;
|
|
77
88
|
}
|
|
78
89
|
|
|
@@ -3,13 +3,18 @@ import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
|
|
|
3
3
|
|
|
4
4
|
import { DEVICE } from '../../events';
|
|
5
5
|
import { assertCompleteDeviceSession } from './deviceSession';
|
|
6
|
-
import { isDeviceLockedError } from './lockedError';
|
|
7
6
|
|
|
8
|
-
import type { DeviceSessionGet } from '@onekeyfe/hd-transport';
|
|
7
|
+
import type { DeviceSessionAskPassphrase, DeviceSessionGet } from '@onekeyfe/hd-transport';
|
|
9
8
|
import type { Device } from '../../device/Device';
|
|
10
9
|
|
|
11
10
|
const HOST_PASSPHRASE_MAX_BYTES = 50;
|
|
12
11
|
|
|
12
|
+
const isWalletSessionInvalidError = (error: unknown) =>
|
|
13
|
+
typeof error === 'object' &&
|
|
14
|
+
error !== null &&
|
|
15
|
+
'errorCode' in error &&
|
|
16
|
+
(error as { errorCode?: unknown }).errorCode === HardwareErrorCode.WalletSessionInvalid;
|
|
17
|
+
|
|
13
18
|
const utf8ByteLength = (value: string): number | undefined => {
|
|
14
19
|
let length = 0;
|
|
15
20
|
for (const character of value) {
|
|
@@ -39,34 +44,11 @@ const negotiateEventlessWalletSession = async (device: Device) => {
|
|
|
39
44
|
});
|
|
40
45
|
};
|
|
41
46
|
|
|
42
|
-
const getDeviceSession = async (device: Device, request: DeviceSessionGet) =>
|
|
43
|
-
|
|
44
|
-
return await device.commands.typedCall('DeviceSessionGet', 'DeviceSession', request);
|
|
45
|
-
} catch (error) {
|
|
46
|
-
if (!isDeviceLockedError(error) || !request.session_id) {
|
|
47
|
-
throw error;
|
|
48
|
-
}
|
|
49
|
-
await device.unlockDevice(DeviceSessionPinType.Main);
|
|
50
|
-
return device.commands.typedCall('DeviceSessionGet', 'DeviceSession', request);
|
|
51
|
-
}
|
|
52
|
-
};
|
|
47
|
+
const getDeviceSession = async (device: Device, request: DeviceSessionGet) =>
|
|
48
|
+
device.commands.typedCall('DeviceSessionGet', 'DeviceSession', request);
|
|
53
49
|
|
|
54
|
-
const askDevicePassphrase = async (device: Device,
|
|
55
|
-
|
|
56
|
-
device.commands.typedCall(
|
|
57
|
-
'DeviceSessionAskPassphrase',
|
|
58
|
-
'Success',
|
|
59
|
-
passphrase ? { passphrase, on_device: false } : { on_device: true }
|
|
60
|
-
);
|
|
61
|
-
try {
|
|
62
|
-
await request();
|
|
63
|
-
} catch (error) {
|
|
64
|
-
if (!isDeviceLockedError(error)) {
|
|
65
|
-
throw error;
|
|
66
|
-
}
|
|
67
|
-
await device.unlockDevice(DeviceSessionPinType.Main);
|
|
68
|
-
await request();
|
|
69
|
-
}
|
|
50
|
+
const askDevicePassphrase = async (device: Device, requestPayload: DeviceSessionAskPassphrase) => {
|
|
51
|
+
await device.commands.typedCall('DeviceSessionAskPassphrase', 'Success', requestPayload);
|
|
70
52
|
await refreshProtocolV2DeviceStatus(device);
|
|
71
53
|
};
|
|
72
54
|
|
|
@@ -126,25 +108,33 @@ const selectDeviceSession = async (device: Device, expectedPassphraseState?: str
|
|
|
126
108
|
}
|
|
127
109
|
|
|
128
110
|
if (hasHostPassphrase) {
|
|
129
|
-
await askDevicePassphrase(device,
|
|
111
|
+
await askDevicePassphrase(device, {
|
|
112
|
+
passphrase: hostPassphrase,
|
|
113
|
+
on_device: false,
|
|
114
|
+
});
|
|
130
115
|
return getDeviceSession(device, {});
|
|
131
116
|
}
|
|
132
117
|
|
|
133
118
|
device.emit(DEVICE.PASSPHRASE_ON_DEVICE, device, metadata);
|
|
134
|
-
await askDevicePassphrase(device);
|
|
119
|
+
await askDevicePassphrase(device, { on_device: true });
|
|
135
120
|
return getDeviceSession(device, {});
|
|
136
121
|
};
|
|
137
122
|
|
|
138
123
|
export async function getProtocolV2WalletSession(
|
|
139
124
|
device: Device,
|
|
140
125
|
options?: {
|
|
126
|
+
forceWalletSelection?: boolean;
|
|
127
|
+
/** @deprecated 兼容旧 getPassphraseState;新流程使用 forceWalletSelection。 */
|
|
141
128
|
initSession?: boolean;
|
|
142
129
|
expectedPassphraseState?: string;
|
|
143
130
|
onlyMainPin?: boolean;
|
|
144
131
|
resumeOnly?: boolean;
|
|
145
132
|
}
|
|
146
133
|
) {
|
|
147
|
-
|
|
134
|
+
const forceWalletSelection =
|
|
135
|
+
options?.forceWalletSelection === true || options?.initSession === true;
|
|
136
|
+
|
|
137
|
+
if (forceWalletSelection) {
|
|
148
138
|
if (options.onlyMainPin) {
|
|
149
139
|
device.clearStandardInternalState?.();
|
|
150
140
|
} else {
|
|
@@ -185,7 +175,10 @@ export async function getProtocolV2WalletSession(
|
|
|
185
175
|
resumed = true;
|
|
186
176
|
} catch (error) {
|
|
187
177
|
device.clearStandardInternalState?.();
|
|
188
|
-
|
|
178
|
+
if (!isWalletSessionInvalidError(error)) {
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
resumed = false;
|
|
189
182
|
}
|
|
190
183
|
}
|
|
191
184
|
|
|
@@ -199,7 +192,10 @@ export async function getProtocolV2WalletSession(
|
|
|
199
192
|
resumed = true;
|
|
200
193
|
} catch (error) {
|
|
201
194
|
device.clearInternalState();
|
|
202
|
-
|
|
195
|
+
if (options?.resumeOnly || !isWalletSessionInvalidError(error)) {
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
resumed = false;
|
|
203
199
|
}
|
|
204
200
|
}
|
|
205
201
|
|
|
@@ -224,6 +220,10 @@ export async function getProtocolV2WalletSession(
|
|
|
224
220
|
}
|
|
225
221
|
if (expectedPassphraseState && expectedPassphraseState !== message.btc_test_address) {
|
|
226
222
|
resumed = false;
|
|
223
|
+
if (options?.resumeOnly) {
|
|
224
|
+
device.clearInternalState();
|
|
225
|
+
throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
|
|
226
|
+
}
|
|
227
227
|
if (options?.onlyMainPin) {
|
|
228
228
|
device.clearStandardInternalState?.();
|
|
229
229
|
await device.unlockDevice(DeviceSessionPinType.Main);
|
|
@@ -258,7 +258,7 @@ export async function getProtocolV2WalletSession(
|
|
|
258
258
|
message.btc_test_address,
|
|
259
259
|
device.getCurrentDeviceId(),
|
|
260
260
|
message.session_id,
|
|
261
|
-
|
|
261
|
+
forceWalletSelection ? null : cachedSessionId ?? null,
|
|
262
262
|
] as const;
|
|
263
263
|
if (options?.onlyMainPin) {
|
|
264
264
|
device.updateInternalState(...internalStateArgs, 'standard');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CommonParams, Response } from '../params';
|
|
2
|
-
import type {
|
|
2
|
+
import type { OnboardingStatus, Success } from '@onekeyfe/hd-transport';
|
|
3
3
|
import type { DeviceRebootParams } from '../../api/protocol-v2/helpers';
|
|
4
4
|
import type {
|
|
5
5
|
DeviceUploadWallpaperParams,
|
|
@@ -45,7 +45,7 @@ export declare function deviceReboot(
|
|
|
45
45
|
export declare function deviceGetOnboardingStatus(
|
|
46
46
|
connectId: string,
|
|
47
47
|
params?: CommonParams
|
|
48
|
-
): Response<
|
|
48
|
+
): Response<OnboardingStatus>;
|
|
49
49
|
|
|
50
50
|
export declare function deviceUploadWallpaper(
|
|
51
51
|
connectId: string,
|
package/src/types/device.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
OneKeyDeviceCommType,
|
|
8
8
|
ProtocolV2DeviceInfo,
|
|
9
9
|
DeviceStatus as ProtocolV2DeviceStatus,
|
|
10
|
+
ProtocolInfo as ProtocolV2ProtocolInfo,
|
|
10
11
|
} from '@onekeyfe/hd-transport';
|
|
11
12
|
|
|
12
13
|
export type DeviceStatus = 'available' | 'occupied' | 'used';
|
|
@@ -159,6 +160,7 @@ export type DeviceFeaturesVerify = {
|
|
|
159
160
|
export type DeviceFeaturesRaw = {
|
|
160
161
|
protocolV1Features?: PROTO.Features;
|
|
161
162
|
protocolV1OneKeyFeatures?: OnekeyFeatures;
|
|
163
|
+
protocolV2ProtocolInfo?: ProtocolV2ProtocolInfo;
|
|
162
164
|
protocolV2DeviceInfo?: ProtocolV2DeviceInfo;
|
|
163
165
|
protocolV2DeviceStatus?: ProtocolV2DeviceStatus;
|
|
164
166
|
};
|