@onekeyfe/hd-core 1.2.0-alpha.10 → 1.2.0-alpha.12
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 +25 -0
- package/__tests__/RequestQueue.test.ts +34 -0
- package/__tests__/pro2Wallpaper.test.ts +50 -0
- package/__tests__/protocol-v2.test.ts +511 -61
- package/__tests__/protocolV2FileWrite.test.ts +55 -0
- package/dist/api/BaseMethod.d.ts +4 -0
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/FileWrite.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +0 -4
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts +1 -6
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts +18 -0
- package/dist/api/UploadPortfolio.d.ts.map +1 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts +32 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -0
- package/dist/api/index.d.ts +6 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +6 -0
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceSessionGet.d.ts +1 -4
- package/dist/api/protocol-v2/DeviceSessionGet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceSettingsGet.d.ts +6 -0
- package/dist/api/protocol-v2/DeviceSettingsGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts +16 -0
- package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceSettingsSet.d.ts +12 -0
- package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +26 -0
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -0
- package/dist/core/RequestQueue.d.ts +2 -0
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +4 -4
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceFeatures.d.ts +2 -2
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
- package/dist/events/call.d.ts +7 -0
- package/dist/events/call.d.ts.map +1 -1
- package/dist/events/core.d.ts +2 -2
- package/dist/events/core.d.ts.map +1 -1
- package/dist/events/iframe.d.ts +1 -0
- package/dist/events/iframe.d.ts.map +1 -1
- package/dist/index.d.ts +80 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +922 -472
- package/dist/inject.d.ts +3 -2
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts +2 -1
- package/dist/lowLevelInject.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/unlockRetry.d.ts +9 -0
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/topLevelInject.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +2 -0
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/getDeviceInfo.d.ts +1 -1
- package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
- package/dist/types/api/getPassphraseState.d.ts +2 -10
- package/dist/types/api/getPassphraseState.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +9 -2
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +20 -4
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +1 -1
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +0 -2
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/dist/utils/pro2Wallpaper.d.ts +12 -0
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +18 -0
- package/src/api/FileWrite.ts +18 -178
- package/src/api/FirmwareUpdateV4.ts +21 -91
- package/src/api/GetPassphraseState.ts +9 -18
- package/src/api/UploadPortfolio.ts +37 -0
- package/src/api/helpers/protocolV2FileWrite.ts +164 -0
- package/src/api/index.ts +6 -0
- package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +18 -0
- package/src/api/protocol-v2/DeviceSessionGet.ts +3 -14
- package/src/api/protocol-v2/DeviceSettingsGet.ts +16 -0
- package/src/api/protocol-v2/DeviceSettingsPageShow.ts +59 -0
- package/src/api/protocol-v2/DeviceSettingsSet.ts +39 -0
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +124 -0
- package/src/core/RequestQueue.ts +19 -0
- package/src/core/index.ts +10 -1
- package/src/data/messages/messages-protocol-v2.json +92 -8
- package/src/device/Device.ts +17 -3
- package/src/device/DeviceCommands.ts +9 -3
- package/src/deviceProfile/buildDeviceFeatures.ts +20 -12
- package/src/deviceProfile/buildDeviceProfile.ts +8 -6
- package/src/events/call.ts +6 -0
- package/src/events/core.ts +2 -0
- package/src/events/iframe.ts +1 -0
- package/src/index.ts +4 -0
- package/src/inject.ts +15 -0
- package/src/lowLevelInject.ts +3 -0
- package/src/protocols/protocol-v2/features.ts +19 -1
- package/src/protocols/protocol-v2/index.ts +2 -0
- package/src/protocols/protocol-v2/unlockRetry.ts +46 -0
- package/src/protocols/protocol-v2/walletSession.ts +14 -5
- package/src/topLevelInject.ts +2 -0
- package/src/types/api/firmwareUpdate.ts +12 -0
- package/src/types/api/getDeviceInfo.ts +1 -1
- package/src/types/api/getPassphraseState.ts +2 -11
- package/src/types/api/index.ts +14 -1
- package/src/types/api/protocolV2.ts +52 -3
- package/src/types/device.ts +1 -0
- package/src/utils/deviceFeaturesUtils.ts +3 -8
- package/src/utils/index.ts +6 -0
- package/src/utils/pro2Wallpaper.ts +111 -0
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
import { BaseMethod } from '../BaseMethod';
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
sessionId?: string;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
export default class DeviceSessionGet extends BaseMethod<DeviceSessionGetParams> {
|
|
3
|
+
export default class DeviceSessionGet extends BaseMethod {
|
|
8
4
|
init() {
|
|
9
5
|
this.requireProtocolV2 = true;
|
|
10
6
|
this.useDevicePassphraseState = false;
|
|
11
7
|
this.skipForceUpdateCheck = true;
|
|
12
|
-
this.params =
|
|
13
|
-
sessionId: this.payload.sessionId,
|
|
14
|
-
};
|
|
8
|
+
this.params = undefined;
|
|
15
9
|
}
|
|
16
10
|
|
|
17
11
|
async run() {
|
|
18
|
-
const
|
|
19
|
-
const { message } = await this.device.commands.typedCall(
|
|
20
|
-
'DeviceSessionGet',
|
|
21
|
-
'DeviceSession',
|
|
22
|
-
payload
|
|
23
|
-
);
|
|
12
|
+
const { message } = await this.device.commands.typedCall('DeviceSessionGet', 'DeviceSession', {});
|
|
24
13
|
return message;
|
|
25
14
|
}
|
|
26
15
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BaseMethod } from '../BaseMethod';
|
|
2
|
+
|
|
3
|
+
export default class DeviceSettingsGet extends BaseMethod {
|
|
4
|
+
init() {
|
|
5
|
+
this.requireProtocolV2 = true;
|
|
6
|
+
this.unlockPolicy = 'retry-on-locked';
|
|
7
|
+
this.skipForceUpdateCheck = true;
|
|
8
|
+
this.useDevicePassphraseState = false;
|
|
9
|
+
this.params = undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async run() {
|
|
13
|
+
const res = await this.device.commands.typedCall('DeviceSettingsGet', 'DeviceSettings', {});
|
|
14
|
+
return Promise.resolve(res.message);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
|
|
2
|
+
|
|
3
|
+
import { BaseMethod } from '../BaseMethod';
|
|
4
|
+
import { invalidParameter } from '../helpers/filesystemValidation';
|
|
5
|
+
|
|
6
|
+
export type SupportedDeviceSettingsPage = DeviceSettingsPage;
|
|
7
|
+
|
|
8
|
+
export type DeviceSettingsPageShowParams = {
|
|
9
|
+
page?:
|
|
10
|
+
| SupportedDeviceSettingsPage
|
|
11
|
+
| 'DeviceReset'
|
|
12
|
+
| 'DevicePinChange'
|
|
13
|
+
| 'DevicePassphrase'
|
|
14
|
+
| 'DeviceAirgap';
|
|
15
|
+
fieldName?: string;
|
|
16
|
+
field_name?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const SUPPORTED_PAGES = new Set<number>([
|
|
20
|
+
DeviceSettingsPage.DeviceReset,
|
|
21
|
+
DeviceSettingsPage.DevicePinChange,
|
|
22
|
+
DeviceSettingsPage.DevicePassphrase,
|
|
23
|
+
DeviceSettingsPage.DeviceAirgap,
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
function normalizePage(value: DeviceSettingsPageShowParams['page']): SupportedDeviceSettingsPage {
|
|
27
|
+
const page = typeof value === 'string' ? DeviceSettingsPage[value] : value;
|
|
28
|
+
if (page !== undefined && SUPPORTED_PAGES.has(page)) {
|
|
29
|
+
return page as SupportedDeviceSettingsPage;
|
|
30
|
+
}
|
|
31
|
+
throw invalidParameter(
|
|
32
|
+
'Parameter [page] must be DeviceReset, DevicePinChange, DevicePassphrase, or DeviceAirgap.'
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default class DeviceSettingsPageShow extends BaseMethod<{
|
|
37
|
+
page: SupportedDeviceSettingsPage;
|
|
38
|
+
field_name?: string;
|
|
39
|
+
}> {
|
|
40
|
+
init() {
|
|
41
|
+
this.requireProtocolV2 = true;
|
|
42
|
+
this.unlockPolicy = 'retry-on-locked';
|
|
43
|
+
this.skipForceUpdateCheck = true;
|
|
44
|
+
this.useDevicePassphraseState = false;
|
|
45
|
+
this.params = {
|
|
46
|
+
page: normalizePage(this.payload.page),
|
|
47
|
+
field_name: this.payload.field_name ?? this.payload.fieldName,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async run() {
|
|
52
|
+
const res = await this.device.commands.typedCall(
|
|
53
|
+
'DeviceSettingsPageShow',
|
|
54
|
+
'Success',
|
|
55
|
+
this.params
|
|
56
|
+
);
|
|
57
|
+
return Promise.resolve(res.message);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BaseMethod } from '../BaseMethod';
|
|
2
|
+
import { invalidParameter } from '../helpers/filesystemValidation';
|
|
3
|
+
|
|
4
|
+
import type { DeviceSettings } from '@onekeyfe/hd-transport';
|
|
5
|
+
|
|
6
|
+
export type DeviceSettingsSetParams = {
|
|
7
|
+
settings?: Omit<DeviceSettings, 'passphrase_enable' | 'airgap_mode'>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default class DeviceSettingsSet extends BaseMethod<{
|
|
11
|
+
settings: Omit<DeviceSettings, 'passphrase_enable' | 'airgap_mode'>;
|
|
12
|
+
}> {
|
|
13
|
+
init() {
|
|
14
|
+
const settings = this.payload.settings;
|
|
15
|
+
if (!settings || typeof settings !== 'object' || Array.isArray(settings)) {
|
|
16
|
+
throw invalidParameter('Parameter [settings] must be an object.');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
passphrase_enable: _passphraseEnable,
|
|
21
|
+
airgap_mode: _airgapMode,
|
|
22
|
+
...supported
|
|
23
|
+
} = settings as DeviceSettings;
|
|
24
|
+
if (Object.keys(supported).length === 0) {
|
|
25
|
+
throw invalidParameter('Parameter [settings] must contain at least one supported setting.');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
this.requireProtocolV2 = true;
|
|
29
|
+
this.unlockPolicy = 'retry-on-locked';
|
|
30
|
+
this.skipForceUpdateCheck = true;
|
|
31
|
+
this.useDevicePassphraseState = false;
|
|
32
|
+
this.params = { settings: supported };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async run() {
|
|
36
|
+
const res = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', this.params);
|
|
37
|
+
return Promise.resolve(res.message);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { blake2s } from '@noble/hashes/blake2s';
|
|
2
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
3
|
+
|
|
4
|
+
import { BaseMethod } from '../BaseMethod';
|
|
5
|
+
import { invalidParameter } from '../helpers/filesystemValidation';
|
|
6
|
+
import { writeProtocolV2File } from '../helpers/protocolV2FileWrite';
|
|
7
|
+
import {
|
|
8
|
+
encodePro2Wallpaper,
|
|
9
|
+
PRO2_WALLPAPER_HEIGHT,
|
|
10
|
+
PRO2_WALLPAPER_WIDTH,
|
|
11
|
+
type Pro2WallpaperColorFormat,
|
|
12
|
+
} from '../../utils/pro2Wallpaper';
|
|
13
|
+
|
|
14
|
+
export type DeviceUploadWallpaperParams = {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
rgba: Uint8Array | ArrayBuffer;
|
|
18
|
+
fileName?: string;
|
|
19
|
+
chunkSize?: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type DeviceUploadWallpaperResponse = {
|
|
23
|
+
path: string;
|
|
24
|
+
size: number;
|
|
25
|
+
colorFormat: Pro2WallpaperColorFormat;
|
|
26
|
+
message?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const WALLPAPER_DIRECTORY = 'vol0:/wallpapers/user';
|
|
30
|
+
const SAFE_FILE_NAME = /^[A-Za-z0-9_-]+(?:\.bin)?$/;
|
|
31
|
+
|
|
32
|
+
function normalizeFileName(fileName: string | undefined, data: Uint8Array): string {
|
|
33
|
+
if (fileName !== undefined && (!fileName || !SAFE_FILE_NAME.test(fileName))) {
|
|
34
|
+
throw invalidParameter(
|
|
35
|
+
'Parameter [fileName] may only contain letters, numbers, underscores, hyphens and an optional .bin suffix.'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const baseName = fileName ?? `wallpaper-${bytesToHex(blake2s(data)).slice(0, 12)}`;
|
|
39
|
+
return baseName.endsWith('.bin') ? baseName : `${baseName}.bin`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpaperParams> {
|
|
43
|
+
private encoded?: { data: Uint8Array; colorFormat: Pro2WallpaperColorFormat };
|
|
44
|
+
|
|
45
|
+
private directoryReady = false;
|
|
46
|
+
|
|
47
|
+
private uploaded = false;
|
|
48
|
+
|
|
49
|
+
private path = '';
|
|
50
|
+
|
|
51
|
+
init() {
|
|
52
|
+
const { width, height, rgba, fileName, chunkSize } = this.payload;
|
|
53
|
+
if (width !== PRO2_WALLPAPER_WIDTH || height !== PRO2_WALLPAPER_HEIGHT) {
|
|
54
|
+
throw invalidParameter(
|
|
55
|
+
`Pro2 wallpaper dimensions must be ${PRO2_WALLPAPER_WIDTH}x${PRO2_WALLPAPER_HEIGHT}.`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (!(rgba instanceof ArrayBuffer) && !ArrayBuffer.isView(rgba)) {
|
|
59
|
+
throw invalidParameter('Parameter [rgba] must be an ArrayBuffer or Uint8Array.');
|
|
60
|
+
}
|
|
61
|
+
if (chunkSize !== undefined && (!Number.isInteger(chunkSize) || chunkSize <= 0)) {
|
|
62
|
+
throw invalidParameter('Parameter [chunkSize] must be a positive integer.');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const rgbaBytes =
|
|
66
|
+
rgba instanceof ArrayBuffer
|
|
67
|
+
? rgba
|
|
68
|
+
: new Uint8Array(rgba.buffer, rgba.byteOffset, rgba.byteLength);
|
|
69
|
+
this.encoded = encodePro2Wallpaper({ width, height, rgba: rgbaBytes });
|
|
70
|
+
this.path = `${WALLPAPER_DIRECTORY}/${normalizeFileName(fileName, this.encoded.data)}`;
|
|
71
|
+
this.params = { width, height, rgba: rgbaBytes, fileName, chunkSize };
|
|
72
|
+
this.requireProtocolV2 = true;
|
|
73
|
+
this.unlockPolicy = 'retry-on-locked';
|
|
74
|
+
this.skipForceUpdateCheck = true;
|
|
75
|
+
this.useDevicePassphraseState = false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private async ensureDirectory() {
|
|
79
|
+
if (this.directoryReady) return;
|
|
80
|
+
try {
|
|
81
|
+
await this.device.commands.typedCall('FilesystemDirMake', 'Success', {
|
|
82
|
+
path: WALLPAPER_DIRECTORY,
|
|
83
|
+
});
|
|
84
|
+
} catch (error) {
|
|
85
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
86
|
+
if (!/exist/i.test(message)) throw error;
|
|
87
|
+
}
|
|
88
|
+
this.directoryReady = true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private async upload() {
|
|
92
|
+
if (this.uploaded) return;
|
|
93
|
+
const encoded = this.encoded;
|
|
94
|
+
if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
|
|
95
|
+
|
|
96
|
+
await writeProtocolV2File({
|
|
97
|
+
commands: this.device.commands,
|
|
98
|
+
path: this.path,
|
|
99
|
+
data: encoded.data,
|
|
100
|
+
totalSize: encoded.data.byteLength,
|
|
101
|
+
chunkSize: this.params.chunkSize,
|
|
102
|
+
overwrite: true,
|
|
103
|
+
append: false,
|
|
104
|
+
throwIfAborted: () => this.throwIfAborted(),
|
|
105
|
+
});
|
|
106
|
+
this.uploaded = true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async run(): Promise<DeviceUploadWallpaperResponse> {
|
|
110
|
+
const encoded = this.encoded;
|
|
111
|
+
if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
|
|
112
|
+
await this.ensureDirectory();
|
|
113
|
+
await this.upload();
|
|
114
|
+
const response = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
|
|
115
|
+
settings: { wallpaper_path: this.path },
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
path: this.path,
|
|
119
|
+
size: encoded.data.byteLength,
|
|
120
|
+
colorFormat: encoded.colorFormat,
|
|
121
|
+
message: response.message?.message,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
package/src/core/RequestQueue.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type RequestTask = {
|
|
|
14
14
|
export default class RequestQueue {
|
|
15
15
|
private requestQueue = new Map<number, RequestTask>();
|
|
16
16
|
|
|
17
|
+
private operationRequestIds = new Map<string, number>();
|
|
18
|
+
|
|
17
19
|
private pendingCallbackTasks = new Map<string, Deferred<void>>();
|
|
18
20
|
|
|
19
21
|
// 生成唯一请求ID
|
|
@@ -30,11 +32,21 @@ export default class RequestQueue {
|
|
|
30
32
|
method.responseID = requestId;
|
|
31
33
|
}
|
|
32
34
|
const abortController = new AbortController();
|
|
35
|
+
method.abortSignal = abortController.signal;
|
|
33
36
|
const task = { id: requestId, method, abortController };
|
|
34
37
|
this.requestQueue.set(requestId, task);
|
|
38
|
+
const operationId = method.payload?.operationId;
|
|
39
|
+
if (typeof operationId === 'string' && operationId) {
|
|
40
|
+
this.operationRequestIds.set(operationId, requestId);
|
|
41
|
+
}
|
|
35
42
|
return task;
|
|
36
43
|
}
|
|
37
44
|
|
|
45
|
+
public abortOperation(operationId: string) {
|
|
46
|
+
const requestId = this.operationRequestIds.get(operationId);
|
|
47
|
+
return requestId === undefined ? false : this.abortRequest(requestId);
|
|
48
|
+
}
|
|
49
|
+
|
|
38
50
|
public getTask(requestId: number): RequestTask | undefined {
|
|
39
51
|
return this.requestQueue.get(requestId);
|
|
40
52
|
}
|
|
@@ -105,6 +117,13 @@ export default class RequestQueue {
|
|
|
105
117
|
|
|
106
118
|
// 删除请求
|
|
107
119
|
public releaseTask(requestId: number) {
|
|
120
|
+
const operationId = this.requestQueue.get(requestId)?.method.payload?.operationId;
|
|
121
|
+
if (
|
|
122
|
+
typeof operationId === 'string' &&
|
|
123
|
+
this.operationRequestIds.get(operationId) === requestId
|
|
124
|
+
) {
|
|
125
|
+
this.operationRequestIds.delete(operationId);
|
|
126
|
+
}
|
|
108
127
|
this.requestQueue.delete(requestId);
|
|
109
128
|
}
|
|
110
129
|
|
package/src/core/index.ts
CHANGED
|
@@ -56,6 +56,7 @@ import TransportManager from '../data-manager/TransportManager';
|
|
|
56
56
|
import DeviceConnector from '../device/DeviceConnector';
|
|
57
57
|
import RequestQueue from './RequestQueue';
|
|
58
58
|
import { getSynchronize } from '../utils/getSynchronize';
|
|
59
|
+
import { runMethodWithUnlockRetry } from '../protocols/protocol-v2/unlockRetry';
|
|
59
60
|
|
|
60
61
|
import type { ConnectSettings, Features, KnownDevice } from '../types';
|
|
61
62
|
import type { CoreMessage, IFrameCallMessage, UiPromise, UiPromiseResponse } from '../events';
|
|
@@ -616,7 +617,7 @@ const onCallDevice = async (
|
|
|
616
617
|
method.device?.commands?.checkDisposed();
|
|
617
618
|
|
|
618
619
|
try {
|
|
619
|
-
const response: object = await method
|
|
620
|
+
const response: object = await runMethodWithUnlockRetry(method, device);
|
|
620
621
|
messageResponse = createResponseMessage(method.responseID, true, response);
|
|
621
622
|
requestQueue.resolveRequest(method.responseID, messageResponse);
|
|
622
623
|
completeMethodRequestContext(method);
|
|
@@ -1341,6 +1342,10 @@ export default class Core extends EventEmitter {
|
|
|
1341
1342
|
Log.debug(`[Core] Created SDK instance: ${this.sdkInstanceId}`);
|
|
1342
1343
|
}
|
|
1343
1344
|
|
|
1345
|
+
cancelOperation(operationId: string) {
|
|
1346
|
+
this.requestQueue.abortOperation(operationId);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1344
1349
|
private getCoreContext() {
|
|
1345
1350
|
return {
|
|
1346
1351
|
sdkInstanceId: this.sdkInstanceId,
|
|
@@ -1409,6 +1414,10 @@ export default class Core extends EventEmitter {
|
|
|
1409
1414
|
cancel(this.getCoreContext(), message.payload.connectId);
|
|
1410
1415
|
break;
|
|
1411
1416
|
}
|
|
1417
|
+
case IFRAME.CANCEL_OPERATION: {
|
|
1418
|
+
this.cancelOperation(message.payload.operationId);
|
|
1419
|
+
break;
|
|
1420
|
+
}
|
|
1412
1421
|
case IFRAME.CALLBACK: {
|
|
1413
1422
|
Log.log('callback message: ', message);
|
|
1414
1423
|
postMessage(message);
|
|
@@ -467,6 +467,7 @@
|
|
|
467
467
|
"MessageType_DeviceSettings": 60410,
|
|
468
468
|
"MessageType_DeviceSettingsGet": 60411,
|
|
469
469
|
"MessageType_DeviceSettingsSet": 60412,
|
|
470
|
+
"MessageType_DeviceSettingsPageShow": 60413,
|
|
470
471
|
"MessageType_DeviceCertificate": 60420,
|
|
471
472
|
"MessageType_DeviceCertificateWrite": 60421,
|
|
472
473
|
"MessageType_DeviceCertificateRead": 60422,
|
|
@@ -7045,14 +7046,6 @@
|
|
|
7045
7046
|
"passphrase_state": {
|
|
7046
7047
|
"type": "string",
|
|
7047
7048
|
"id": 1
|
|
7048
|
-
},
|
|
7049
|
-
"_only_main_pin": {
|
|
7050
|
-
"type": "bool",
|
|
7051
|
-
"id": 2
|
|
7052
|
-
},
|
|
7053
|
-
"allow_create_attach_pin": {
|
|
7054
|
-
"type": "bool",
|
|
7055
|
-
"id": 3
|
|
7056
7049
|
}
|
|
7057
7050
|
}
|
|
7058
7051
|
},
|
|
@@ -11394,6 +11387,20 @@
|
|
|
11394
11387
|
}
|
|
11395
11388
|
}
|
|
11396
11389
|
},
|
|
11390
|
+
"DeviceErrorCode": {
|
|
11391
|
+
"values": {
|
|
11392
|
+
"DeviceError_None": 0,
|
|
11393
|
+
"DeviceError_Busy": 1,
|
|
11394
|
+
"DeviceError_NotInitialized": 2,
|
|
11395
|
+
"DeviceError_ActionCancelled": 3,
|
|
11396
|
+
"DeviceError_PinAlreadyUsed": 4,
|
|
11397
|
+
"DeviceError_PersistFailed": 5,
|
|
11398
|
+
"DeviceError_SeError": 6,
|
|
11399
|
+
"DeviceError_InvalidLanguage": 7,
|
|
11400
|
+
"DeviceError_WallpaperNotUsable": 8,
|
|
11401
|
+
"DeviceError_DeviceLocked": 9
|
|
11402
|
+
}
|
|
11403
|
+
},
|
|
11397
11404
|
"DeviceRebootType": {
|
|
11398
11405
|
"values": {
|
|
11399
11406
|
"Normal": 0,
|
|
@@ -11423,6 +11430,62 @@
|
|
|
11423
11430
|
"language": {
|
|
11424
11431
|
"type": "string",
|
|
11425
11432
|
"id": 3
|
|
11433
|
+
},
|
|
11434
|
+
"wallpaper_path": {
|
|
11435
|
+
"type": "string",
|
|
11436
|
+
"id": 4
|
|
11437
|
+
},
|
|
11438
|
+
"passphrase_enable": {
|
|
11439
|
+
"type": "bool",
|
|
11440
|
+
"id": 6
|
|
11441
|
+
},
|
|
11442
|
+
"brightness": {
|
|
11443
|
+
"type": "uint32",
|
|
11444
|
+
"id": 7
|
|
11445
|
+
},
|
|
11446
|
+
"autolock_delay_ms": {
|
|
11447
|
+
"type": "uint32",
|
|
11448
|
+
"id": 8
|
|
11449
|
+
},
|
|
11450
|
+
"autoshutdown_delay_ms": {
|
|
11451
|
+
"type": "uint32",
|
|
11452
|
+
"id": 9
|
|
11453
|
+
},
|
|
11454
|
+
"animation_enable": {
|
|
11455
|
+
"type": "bool",
|
|
11456
|
+
"id": 10
|
|
11457
|
+
},
|
|
11458
|
+
"tap_to_wake": {
|
|
11459
|
+
"type": "bool",
|
|
11460
|
+
"id": 11
|
|
11461
|
+
},
|
|
11462
|
+
"haptic_feedback": {
|
|
11463
|
+
"type": "bool",
|
|
11464
|
+
"id": 12
|
|
11465
|
+
},
|
|
11466
|
+
"device_name_display_enabled": {
|
|
11467
|
+
"type": "bool",
|
|
11468
|
+
"id": 13
|
|
11469
|
+
},
|
|
11470
|
+
"airgap_mode": {
|
|
11471
|
+
"type": "bool",
|
|
11472
|
+
"id": 14
|
|
11473
|
+
},
|
|
11474
|
+
"fido_enabled": {
|
|
11475
|
+
"type": "bool",
|
|
11476
|
+
"id": 15
|
|
11477
|
+
},
|
|
11478
|
+
"experimental_features": {
|
|
11479
|
+
"type": "bool",
|
|
11480
|
+
"id": 16
|
|
11481
|
+
},
|
|
11482
|
+
"usb_lock_enable": {
|
|
11483
|
+
"type": "bool",
|
|
11484
|
+
"id": 17
|
|
11485
|
+
},
|
|
11486
|
+
"random_keypad": {
|
|
11487
|
+
"type": "bool",
|
|
11488
|
+
"id": 18
|
|
11426
11489
|
}
|
|
11427
11490
|
}
|
|
11428
11491
|
},
|
|
@@ -11438,6 +11501,27 @@
|
|
|
11438
11501
|
}
|
|
11439
11502
|
}
|
|
11440
11503
|
},
|
|
11504
|
+
"DeviceSettingsPage": {
|
|
11505
|
+
"values": {
|
|
11506
|
+
"DeviceReset": 0,
|
|
11507
|
+
"DevicePinChange": 1,
|
|
11508
|
+
"DevicePassphrase": 2,
|
|
11509
|
+
"DeviceAirgap": 3
|
|
11510
|
+
}
|
|
11511
|
+
},
|
|
11512
|
+
"DeviceSettingsPageShow": {
|
|
11513
|
+
"fields": {
|
|
11514
|
+
"page": {
|
|
11515
|
+
"rule": "required",
|
|
11516
|
+
"type": "DeviceSettingsPage",
|
|
11517
|
+
"id": 1
|
|
11518
|
+
},
|
|
11519
|
+
"field_name": {
|
|
11520
|
+
"type": "string",
|
|
11521
|
+
"id": 2
|
|
11522
|
+
}
|
|
11523
|
+
}
|
|
11524
|
+
},
|
|
11441
11525
|
"DeviceCertificate": {
|
|
11442
11526
|
"fields": {
|
|
11443
11527
|
"cert_and_pubkey": {
|
package/src/device/Device.ts
CHANGED
|
@@ -55,7 +55,6 @@ import {
|
|
|
55
55
|
PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
|
|
56
56
|
requestProtocolV2DeviceInfo,
|
|
57
57
|
} from '../protocols/protocol-v2/features';
|
|
58
|
-
import { refreshProtocolV2DeviceStatus } from '../protocols/protocol-v2/walletSession';
|
|
59
58
|
import { buildProtocolV1FeaturesPayload, buildProtocolV2FeaturesPayload } from '../deviceProfile';
|
|
60
59
|
|
|
61
60
|
import type { PROTO } from '../constants';
|
|
@@ -68,6 +67,7 @@ import type { PassphrasePromptResponse } from './DeviceCommands';
|
|
|
68
67
|
import type { Deferred, HardwareConnectProtocol } from '@onekeyfe/hd-shared';
|
|
69
68
|
import type {
|
|
70
69
|
OneKeyDeviceInfo as DeviceDescriptor,
|
|
70
|
+
DeviceSessionPinResult,
|
|
71
71
|
DeviceStatus,
|
|
72
72
|
ProtocolV2DeviceInfo,
|
|
73
73
|
Success,
|
|
@@ -1190,8 +1190,12 @@ export class Device extends EventEmitter {
|
|
|
1190
1190
|
|
|
1191
1191
|
async unlockDevice() {
|
|
1192
1192
|
if (this.isProtocolV2()) {
|
|
1193
|
+
let pinResult: DeviceSessionPinResult;
|
|
1193
1194
|
try {
|
|
1194
|
-
await this.commands.typedCall(
|
|
1195
|
+
({ message: pinResult } = await this.commands.typedCall(
|
|
1196
|
+
'DeviceSessionAskPin',
|
|
1197
|
+
'DeviceSessionPinResult'
|
|
1198
|
+
));
|
|
1195
1199
|
} catch (error) {
|
|
1196
1200
|
const errorText =
|
|
1197
1201
|
error instanceof Error
|
|
@@ -1203,7 +1207,17 @@ export class Device extends EventEmitter {
|
|
|
1203
1207
|
throw error;
|
|
1204
1208
|
}
|
|
1205
1209
|
|
|
1206
|
-
|
|
1210
|
+
const status: DeviceStatus = {};
|
|
1211
|
+
if (pinResult.unlocked != null) {
|
|
1212
|
+
status.unlocked = pinResult.unlocked;
|
|
1213
|
+
}
|
|
1214
|
+
if (pinResult.unlocked_attach_pin != null) {
|
|
1215
|
+
status.unlocked_by_attach_to_pin = pinResult.unlocked_attach_pin;
|
|
1216
|
+
}
|
|
1217
|
+
if (pinResult.passphrase_protection != null) {
|
|
1218
|
+
status.passphrase_enabled = pinResult.passphrase_protection;
|
|
1219
|
+
}
|
|
1220
|
+
return this.updateProtocolV2Status(status);
|
|
1207
1221
|
}
|
|
1208
1222
|
|
|
1209
1223
|
const firmwareVersion = this.getCurrentFirmwareVersionString() ?? '0.0.0';
|
|
@@ -532,8 +532,9 @@ export class DeviceCommands {
|
|
|
532
532
|
|
|
533
533
|
this.device.clearCancelableAction();
|
|
534
534
|
if (res.type === 'Failure') {
|
|
535
|
-
const { code, message } = res.message as {
|
|
535
|
+
const { code, subcode, message } = res.message as {
|
|
536
536
|
code?: string | FailureType;
|
|
537
|
+
subcode?: number;
|
|
537
538
|
message?: string;
|
|
538
539
|
};
|
|
539
540
|
let error: HardwareError | null = null;
|
|
@@ -577,8 +578,13 @@ export class DeviceCommands {
|
|
|
577
578
|
}
|
|
578
579
|
|
|
579
580
|
if (code === 'Failure_ProcessError') {
|
|
580
|
-
|
|
581
|
-
|
|
581
|
+
if (subcode === 9) {
|
|
582
|
+
error = ERRORS.TypedError(HardwareErrorCode.DeviceLocked, message, {
|
|
583
|
+
failureCode: code,
|
|
584
|
+
subcode,
|
|
585
|
+
firmwareMessage: message,
|
|
586
|
+
});
|
|
587
|
+
} else if (
|
|
582
588
|
message?.includes('Bootloader file verify failed') ||
|
|
583
589
|
message?.includes('verify failed')
|
|
584
590
|
) {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
isProtocolV2BootloaderDeviceInfo,
|
|
5
|
+
isProtocolV2RomloaderDeviceInfo,
|
|
6
|
+
} from '../protocols/protocol-v2/features';
|
|
7
|
+
|
|
5
8
|
import type { DeviceFirmwareImageInfo, ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
|
|
6
|
-
import {
|
|
9
|
+
import type { PROTO } from '../constants';
|
|
10
|
+
import type { DeviceFeaturesMode, Features } from '../types';
|
|
7
11
|
|
|
8
12
|
type ProtocolV1FeaturesCompat = PROTO.Features &
|
|
9
13
|
Partial<PROTO.OnekeyFeatures> & {
|
|
@@ -204,7 +208,7 @@ export const buildProtocolV1FeaturesPayload = (
|
|
|
204
208
|
},
|
|
205
209
|
sessionId,
|
|
206
210
|
raw: {
|
|
207
|
-
protocolV1Features
|
|
211
|
+
protocolV1Features,
|
|
208
212
|
},
|
|
209
213
|
};
|
|
210
214
|
};
|
|
@@ -223,7 +227,7 @@ export const buildProtocolV2FeaturesPayload = (
|
|
|
223
227
|
const info = deviceInfo;
|
|
224
228
|
const fwApplication = info?.fw?.application;
|
|
225
229
|
const fwBootloader = info?.fw?.bootloader;
|
|
226
|
-
const fwBoard =
|
|
230
|
+
const fwBoard = info?.fw?.romloader;
|
|
227
231
|
const bleApplication = info?.coprocessor?.application;
|
|
228
232
|
const status = info?.status;
|
|
229
233
|
|
|
@@ -249,14 +253,18 @@ export const buildProtocolV2FeaturesPayload = (
|
|
|
249
253
|
const unlocked = firstValue(status?.unlocked, previous?.unlocked) ?? null;
|
|
250
254
|
const attachToPinEnabled = status?.attach_to_pin_enabled ?? null;
|
|
251
255
|
const unlockedAttachPin = status?.unlocked_by_attach_to_pin ?? undefined;
|
|
256
|
+
const romloaderMode = isProtocolV2RomloaderDeviceInfo(info);
|
|
252
257
|
const bootloaderMode = isProtocolV2BootloaderDeviceInfo(info);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
258
|
+
let mode: DeviceFeaturesMode = 'unknown';
|
|
259
|
+
if (romloaderMode) {
|
|
260
|
+
mode = 'romloader';
|
|
261
|
+
} else if (bootloaderMode) {
|
|
262
|
+
mode = 'bootloader';
|
|
263
|
+
} else if (initialized === false) {
|
|
264
|
+
mode = 'notInitialized';
|
|
265
|
+
} else if (initialized === true) {
|
|
266
|
+
mode = 'normal';
|
|
267
|
+
}
|
|
260
268
|
|
|
261
269
|
return {
|
|
262
270
|
protocol: 'V2',
|
|
@@ -13,6 +13,10 @@ import {
|
|
|
13
13
|
getDeviceUUID,
|
|
14
14
|
getFirmwareType,
|
|
15
15
|
} from '../utils/deviceInfoUtils';
|
|
16
|
+
import {
|
|
17
|
+
isProtocolV2BootloaderDeviceInfo,
|
|
18
|
+
isProtocolV2RomloaderDeviceInfo,
|
|
19
|
+
} from '../protocols/protocol-v2/features';
|
|
16
20
|
|
|
17
21
|
import type {
|
|
18
22
|
DeviceInfoProtocol,
|
|
@@ -26,7 +30,6 @@ import type {
|
|
|
26
30
|
} from '../types/api/getDeviceInfo';
|
|
27
31
|
import type { Features, OnekeyFeatures } from '../types';
|
|
28
32
|
import type { DeviceFirmwareImageInfo, ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
|
|
29
|
-
import { isProtocolV2BootloaderDeviceInfo } from '../protocols/protocol-v2/features';
|
|
30
33
|
|
|
31
34
|
type BuildProtocolV1ProfileParams = {
|
|
32
35
|
protocol?: DeviceInfoProtocol;
|
|
@@ -87,6 +90,7 @@ const getDeviceMode = (features?: Features): DeviceInfoStatus['mode'] => {
|
|
|
87
90
|
};
|
|
88
91
|
|
|
89
92
|
const getProtocolV2Mode = (deviceInfo?: ProtocolV2DeviceInfo): DeviceInfoStatus['mode'] => {
|
|
93
|
+
if (isProtocolV2RomloaderDeviceInfo(deviceInfo)) return 'romloader';
|
|
90
94
|
if (isProtocolV2BootloaderDeviceInfo(deviceInfo)) return 'bootloader';
|
|
91
95
|
const initialized = deviceInfo?.status?.init_states;
|
|
92
96
|
if (initialized === false) return 'notInitialized';
|
|
@@ -154,9 +158,7 @@ const normalizeV2Versions = (deviceInfo?: ProtocolV2DeviceInfo): DeviceProfileVe
|
|
|
154
158
|
return {
|
|
155
159
|
firmware: firstMeaningfulVersion(getImageVersion(info?.fw?.application)),
|
|
156
160
|
bootloader: firstMeaningfulVersion(getImageVersion(info?.fw?.bootloader)),
|
|
157
|
-
board: firstMeaningfulVersion(
|
|
158
|
-
getImageVersion(info?.fw?.application_data ?? info?.fw?.romloader)
|
|
159
|
-
),
|
|
161
|
+
board: firstMeaningfulVersion(getImageVersion(info?.fw?.romloader)),
|
|
160
162
|
ble: firstMeaningfulVersion(getImageVersion(info?.coprocessor?.application)),
|
|
161
163
|
se01: firstMeaningfulVersion(getImageVersion(info?.se1?.application)),
|
|
162
164
|
se02: firstMeaningfulVersion(getImageVersion(info?.se2?.application)),
|
|
@@ -245,8 +247,8 @@ const normalizeV2Verify = (deviceInfo?: ProtocolV2DeviceInfo): DeviceProfileVeri
|
|
|
245
247
|
firmwareHash: getImageHash(info?.fw?.application),
|
|
246
248
|
bootloaderBuildId: getImageBuildId(info?.fw?.bootloader),
|
|
247
249
|
bootloaderHash: getImageHash(info?.fw?.bootloader),
|
|
248
|
-
boardBuildId: getImageBuildId(info?.fw?.
|
|
249
|
-
boardHash: getImageHash(info?.fw?.
|
|
250
|
+
boardBuildId: getImageBuildId(info?.fw?.romloader),
|
|
251
|
+
boardHash: getImageHash(info?.fw?.romloader),
|
|
250
252
|
bleBuildId: getImageBuildId(info?.coprocessor?.application),
|
|
251
253
|
bleHash: getImageHash(info?.coprocessor?.application),
|
|
252
254
|
se01BuildId: getImageBuildId(info?.se1?.application),
|