@onekeyfe/hd-core 1.2.0-alpha.16 → 1.2.0-alpha.17
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__/deviceFeaturesUtils.test.ts +27 -0
- package/__tests__/evmLedgerLegacySafety.test.ts +3 -0
- package/__tests__/homescreen.test.ts +24 -0
- package/__tests__/protocol-v2.test.ts +1006 -204
- package/__tests__/protocolV2FileWrite.test.ts +28 -3
- package/__tests__/protocolV2UiInteraction.test.ts +266 -0
- package/dist/api/BaseMethod.d.ts +4 -0
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +3 -3
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetDeviceInfo.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/device/DeviceCancel.d.ts.map +1 -1
- package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
- package/dist/api/device/DeviceLock.d.ts.map +1 -1
- package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
- package/dist/api/device/DeviceWipe.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts +1 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceSessionOpen.d.ts +28 -0
- package/dist/api/protocol-v2/DeviceSessionOpen.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceSettingsPageShow.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/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/core/deviceEventRegistration.d.ts +1 -0
- package/dist/core/deviceEventRegistration.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +3 -3
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +3 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts +2 -2
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceFeatures.d.ts +6 -2
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceProfile.d.ts +3 -2
- package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
- package/dist/events/device.d.ts +4 -0
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +18 -2
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +56 -11
- package/dist/index.js +1000 -537
- package/dist/protocols/protocol-v2/features.d.ts +6 -18
- 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/uiInteraction.d.ts +39 -0
- package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/unlockRetry.d.ts +5 -3
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +2 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/getDeviceInfo.d.ts +2 -1
- package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +2 -2
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +4 -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/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/homescreen.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/BaseMethod.ts +10 -2
- package/src/api/FirmwareUpdateV4.ts +27 -73
- package/src/api/GetDeviceInfo.ts +9 -2
- package/src/api/GetPassphraseState.ts +5 -2
- package/src/api/UploadPortfolio.ts +2 -0
- package/src/api/device/DeviceCancel.ts +1 -0
- package/src/api/device/DeviceChangePin.ts +23 -0
- package/src/api/device/DeviceLock.ts +1 -0
- package/src/api/device/DeviceUnlock.ts +9 -0
- package/src/api/device/DeviceWipe.ts +18 -0
- package/src/api/helpers/protocolV2FileWrite.ts +35 -11
- package/src/api/index.ts +1 -1
- package/src/api/protocol-v2/DeviceInfoGet.ts +0 -1
- package/src/api/protocol-v2/DeviceSessionOpen.ts +100 -0
- package/src/api/protocol-v2/DeviceSettingsPageShow.ts +9 -1
- package/src/api/protocol-v2/DeviceSettingsSet.ts +11 -1
- package/src/api/protocol-v2/DeviceStatusGet.ts +1 -0
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +2 -1
- package/src/core/deviceEventRegistration.ts +2 -0
- package/src/core/index.ts +36 -3
- package/src/data/messages/messages-protocol-v2.json +122 -23
- package/src/device/Device.ts +33 -41
- package/src/device/DeviceCommands.ts +22 -4
- package/src/device/DevicePool.ts +14 -6
- package/src/deviceProfile/buildDeviceFeatures.ts +21 -9
- package/src/deviceProfile/buildDeviceProfile.ts +27 -11
- package/src/events/device.ts +4 -0
- package/src/events/ui-request.ts +24 -2
- package/src/inject.ts +2 -2
- package/src/protocols/protocol-v2/features.ts +27 -20
- package/src/protocols/protocol-v2/index.ts +0 -1
- package/src/protocols/protocol-v2/uiInteraction.ts +209 -0
- package/src/protocols/protocol-v2/unlockRetry.ts +43 -3
- package/src/protocols/protocol-v2/walletSession.ts +122 -31
- package/src/types/api/getDeviceInfo.ts +6 -1
- package/src/types/api/index.ts +2 -2
- package/src/types/api/protocolV2.ts +5 -3
- package/src/types/device.ts +6 -1
- package/src/utils/deviceFeaturesUtils.ts +13 -0
- package/src/utils/homescreen.ts +10 -0
- package/dist/api/protocol-v2/DeviceSessionGet.d.ts +0 -6
- package/dist/api/protocol-v2/DeviceSessionGet.d.ts.map +0 -1
- package/src/api/protocol-v2/DeviceSessionGet.ts +0 -15
|
@@ -19,10 +19,13 @@ export default class GetPassphraseState extends BaseMethod {
|
|
|
19
19
|
|
|
20
20
|
const passphraseProtection = this.device.getCurrentPassphraseProtection();
|
|
21
21
|
const deviceType = this.device.getCurrentDeviceType();
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
if (deviceType === EDeviceType.Pro2) {
|
|
24
|
+
return Promise.resolve(passphraseProtection === true ? passphraseState : undefined);
|
|
25
|
+
}
|
|
23
26
|
|
|
24
27
|
return Promise.resolve(
|
|
25
|
-
|
|
28
|
+
deviceType === EDeviceType.Pro || passphraseProtection === true ? passphraseState : undefined
|
|
26
29
|
);
|
|
27
30
|
}
|
|
28
31
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
|
|
2
|
+
|
|
1
3
|
import { BaseMethod } from '../BaseMethod';
|
|
2
4
|
import { validateParams } from '../helpers/paramsValidator';
|
|
5
|
+
import { invalidParameter } from '../helpers/filesystemValidation';
|
|
3
6
|
|
|
4
7
|
import type { ChangePin } from '@onekeyfe/hd-transport';
|
|
5
8
|
|
|
@@ -13,9 +16,29 @@ export default class DeviceChangePin extends BaseMethod<ChangePin> {
|
|
|
13
16
|
this.params = {
|
|
14
17
|
remove: this.payload.remove,
|
|
15
18
|
};
|
|
19
|
+
this.protocolV2UiInteraction = {
|
|
20
|
+
request: 'button',
|
|
21
|
+
source: 'method-lifecycle',
|
|
22
|
+
reason: 'change-pin',
|
|
23
|
+
completion: 'operation-completed',
|
|
24
|
+
deviceOnly: true,
|
|
25
|
+
page: DeviceSettingsPage.DevicePinChange,
|
|
26
|
+
};
|
|
16
27
|
}
|
|
17
28
|
|
|
18
29
|
async run() {
|
|
30
|
+
if (this.device.isProtocolV2()) {
|
|
31
|
+
if (this.params.remove) {
|
|
32
|
+
throw invalidParameter(
|
|
33
|
+
'Parameter [remove=true] is not supported by the Pro2 device PIN page.'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const res = await this.device.commands.typedCall('DeviceSettingsPageShow', 'Success', {
|
|
37
|
+
page: DeviceSettingsPage.DevicePinChange,
|
|
38
|
+
});
|
|
39
|
+
return Promise.resolve(res.message);
|
|
40
|
+
}
|
|
41
|
+
|
|
19
42
|
const res = await this.device.commands.typedCall('ChangePin', 'Success', {
|
|
20
43
|
...this.params,
|
|
21
44
|
});
|
|
@@ -5,6 +5,15 @@ import type { LockDevice } from '@onekeyfe/hd-transport';
|
|
|
5
5
|
export default class DeviceUnlock extends BaseMethod<LockDevice> {
|
|
6
6
|
init() {
|
|
7
7
|
this.useDevicePassphraseState = false;
|
|
8
|
+
this.unlockPolicy = 'none';
|
|
9
|
+
this.protocolV2UiInteraction = {
|
|
10
|
+
request: 'pin',
|
|
11
|
+
source: 'method-lifecycle',
|
|
12
|
+
reason: 'device-unlock',
|
|
13
|
+
completion: 'operation-completed',
|
|
14
|
+
deviceOnly: true,
|
|
15
|
+
operation: 'unlock-device',
|
|
16
|
+
};
|
|
8
17
|
}
|
|
9
18
|
|
|
10
19
|
async run() {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
|
|
2
|
+
|
|
1
3
|
import { BaseMethod } from '../BaseMethod';
|
|
2
4
|
|
|
3
5
|
import type { WipeDevice } from '@onekeyfe/hd-transport';
|
|
@@ -5,9 +7,25 @@ import type { WipeDevice } from '@onekeyfe/hd-transport';
|
|
|
5
7
|
export default class DeviceWipe extends BaseMethod<WipeDevice> {
|
|
6
8
|
init() {
|
|
7
9
|
this.useDevicePassphraseState = false;
|
|
10
|
+
this.protocolV2UiInteraction = {
|
|
11
|
+
request: 'button',
|
|
12
|
+
source: 'method-lifecycle',
|
|
13
|
+
reason: 'device-management',
|
|
14
|
+
completion: 'operation-completed',
|
|
15
|
+
deviceOnly: true,
|
|
16
|
+
page: DeviceSettingsPage.DeviceReset,
|
|
17
|
+
operation: 'wipe-device',
|
|
18
|
+
};
|
|
8
19
|
}
|
|
9
20
|
|
|
10
21
|
async run() {
|
|
22
|
+
if (this.device.isProtocolV2()) {
|
|
23
|
+
const res = await this.device.commands.typedCall('DeviceSettingsPageShow', 'Success', {
|
|
24
|
+
page: DeviceSettingsPage.DeviceReset,
|
|
25
|
+
});
|
|
26
|
+
return Promise.resolve(res.message);
|
|
27
|
+
}
|
|
28
|
+
|
|
11
29
|
const res = await this.device.commands.typedCall('WipeDevice', 'Success');
|
|
12
30
|
|
|
13
31
|
return Promise.resolve(res.message);
|
|
@@ -30,12 +30,23 @@ export type ProtocolV2FileWriteOptions = {
|
|
|
30
30
|
append?: boolean;
|
|
31
31
|
uiPercentage?: number;
|
|
32
32
|
timeoutMs?: number;
|
|
33
|
+
maxChunkRetries?: number;
|
|
33
34
|
throwIfAborted?: () => void;
|
|
34
35
|
onProgress?: (progress: ProtocolV2FileWriteProgress) => void;
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
const MIN_FILE_CHUNK_SIZE = 64;
|
|
38
39
|
|
|
40
|
+
function isRetryableFileWriteTimeout(error: unknown) {
|
|
41
|
+
if (!error || typeof error !== 'object') return false;
|
|
42
|
+
const candidate = error as { errorCode?: number; code?: number; message?: string };
|
|
43
|
+
const code = candidate.errorCode ?? candidate.code;
|
|
44
|
+
return (
|
|
45
|
+
code === HardwareErrorCode.BleTimeoutError ||
|
|
46
|
+
/Lowlevel response timeout/i.test(candidate.message ?? '')
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
39
50
|
function getProtocolV2FileChunkLimit() {
|
|
40
51
|
const env = DataManager.getSettings('env');
|
|
41
52
|
return env && DataManager.isBleConnect(env)
|
|
@@ -116,17 +127,30 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
116
127
|
const progress =
|
|
117
128
|
options.uiPercentage ??
|
|
118
129
|
getDeviceTransferProgress(offset, offset + chunk.byteLength, totalSize);
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
const request = {
|
|
131
|
+
file: { path: options.path, offset, total_size: totalSize, data: chunk },
|
|
132
|
+
overwrite: chunks === 0 ? options.overwrite ?? false : false,
|
|
133
|
+
append: options.append ?? false,
|
|
134
|
+
ui_percentage: progress,
|
|
135
|
+
};
|
|
136
|
+
const maxChunkRetries = Math.max(Math.floor(options.maxChunkRetries ?? 0), 0);
|
|
137
|
+
let retryCount = 0;
|
|
138
|
+
let response;
|
|
139
|
+
while (true) {
|
|
140
|
+
try {
|
|
141
|
+
response = await options.commands.typedCall(
|
|
142
|
+
'FilesystemFileWrite',
|
|
143
|
+
'FilesystemFile',
|
|
144
|
+
request,
|
|
145
|
+
{ timeoutMs: options.timeoutMs }
|
|
146
|
+
);
|
|
147
|
+
break;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (retryCount >= maxChunkRetries || !isRetryableFileWriteTimeout(error)) throw error;
|
|
150
|
+
retryCount += 1;
|
|
151
|
+
options.throwIfAborted?.();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
130
154
|
options.throwIfAborted?.();
|
|
131
155
|
lastMessage = response.message;
|
|
132
156
|
const rawProcessedByte = response.message?.processed_byte;
|
package/src/api/index.ts
CHANGED
|
@@ -50,7 +50,7 @@ export { default as deviceReboot } from './protocol-v2/DeviceReboot';
|
|
|
50
50
|
export { default as deviceInfoGet } from './protocol-v2/DeviceInfoGet';
|
|
51
51
|
export { default as deviceStatusGet } from './protocol-v2/DeviceStatusGet';
|
|
52
52
|
export { default as deviceGetOnboardingStatus } from './protocol-v2/DeviceGetOnboardingStatus';
|
|
53
|
-
export { default as
|
|
53
|
+
export { default as deviceSessionOpen } from './protocol-v2/DeviceSessionOpen';
|
|
54
54
|
export { default as deviceFirmwareUpdate } from './protocol-v2/DeviceFirmwareUpdate';
|
|
55
55
|
export { default as deviceGetFirmwareUpdateStatus } from './protocol-v2/DeviceGetFirmwareUpdateStatus';
|
|
56
56
|
export { default as deviceFactoryInfoSet } from './protocol-v2/DeviceFactoryInfoSet';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { BaseMethod } from '../BaseMethod';
|
|
2
|
+
import { invalidParameter } from '../helpers/filesystemValidation';
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
DeviceAttachPinOnDevice,
|
|
6
|
+
DeviceHostPassphrase,
|
|
7
|
+
DevicePassphraseOnDevice,
|
|
8
|
+
DeviceSessionResume,
|
|
9
|
+
} from '@onekeyfe/hd-transport';
|
|
10
|
+
|
|
11
|
+
type HiddenWalletSelection =
|
|
12
|
+
| {
|
|
13
|
+
host_passphrase: DeviceHostPassphrase;
|
|
14
|
+
passphrase_on_device?: never;
|
|
15
|
+
attach_pin_on_device?: never;
|
|
16
|
+
}
|
|
17
|
+
| {
|
|
18
|
+
host_passphrase?: never;
|
|
19
|
+
passphrase_on_device: DevicePassphraseOnDevice;
|
|
20
|
+
attach_pin_on_device?: never;
|
|
21
|
+
}
|
|
22
|
+
| {
|
|
23
|
+
host_passphrase?: never;
|
|
24
|
+
passphrase_on_device?: never;
|
|
25
|
+
attach_pin_on_device: DeviceAttachPinOnDevice;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type DeviceSessionOpenParams =
|
|
29
|
+
| { resume: DeviceSessionResume; select?: never }
|
|
30
|
+
| { resume?: never; select: HiddenWalletSelection };
|
|
31
|
+
|
|
32
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
33
|
+
!!value && typeof value === 'object' && !Array.isArray(value);
|
|
34
|
+
|
|
35
|
+
const normalizeParams = (payload: Record<string, unknown>): DeviceSessionOpenParams => {
|
|
36
|
+
const hasResume = payload.resume !== undefined;
|
|
37
|
+
const hasSelect = payload.select !== undefined;
|
|
38
|
+
if (hasResume === hasSelect) {
|
|
39
|
+
throw invalidParameter('DeviceSessionOpen requires exactly one of [resume] or [select].');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (hasResume) {
|
|
43
|
+
if (!isRecord(payload.resume) || typeof payload.resume.session_id !== 'string') {
|
|
44
|
+
throw invalidParameter('Parameter [resume.session_id] must be a non-empty string.');
|
|
45
|
+
}
|
|
46
|
+
const sessionId = payload.resume.session_id.trim();
|
|
47
|
+
if (!sessionId) {
|
|
48
|
+
throw invalidParameter('Parameter [resume.session_id] must be a non-empty string.');
|
|
49
|
+
}
|
|
50
|
+
return { resume: { session_id: sessionId } };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!isRecord(payload.select)) {
|
|
54
|
+
throw invalidParameter('Parameter [select] must be a hidden-wallet selection object.');
|
|
55
|
+
}
|
|
56
|
+
const { select } = payload;
|
|
57
|
+
const accessKeys = ['host_passphrase', 'passphrase_on_device', 'attach_pin_on_device'] as const;
|
|
58
|
+
const selected = accessKeys.filter(key => select[key] !== undefined);
|
|
59
|
+
const unknown = Object.keys(select).filter(
|
|
60
|
+
key => !accessKeys.includes(key as (typeof accessKeys)[number])
|
|
61
|
+
);
|
|
62
|
+
if (unknown.length > 0 || selected.length !== 1) {
|
|
63
|
+
throw invalidParameter('Parameter [select] requires exactly one hidden-wallet access mode.');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (selected[0] === 'host_passphrase') {
|
|
67
|
+
const host = select.host_passphrase;
|
|
68
|
+
if (!isRecord(host) || typeof host.passphrase !== 'string') {
|
|
69
|
+
throw invalidParameter('Parameter [select.host_passphrase.passphrase] is required.');
|
|
70
|
+
}
|
|
71
|
+
const passphrase = host.passphrase.normalize('NFKD');
|
|
72
|
+
if (!passphrase || passphrase.includes('\0') || passphrase.length > 50) {
|
|
73
|
+
throw invalidParameter('Parameter [select.host_passphrase.passphrase] is invalid.');
|
|
74
|
+
}
|
|
75
|
+
return { select: { host_passphrase: { passphrase } } };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (selected[0] === 'passphrase_on_device') {
|
|
79
|
+
return { select: { passphrase_on_device: {} } };
|
|
80
|
+
}
|
|
81
|
+
return { select: { attach_pin_on_device: {} } };
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default class DeviceSessionOpen extends BaseMethod<DeviceSessionOpenParams> {
|
|
85
|
+
init() {
|
|
86
|
+
this.requireProtocolV2 = true;
|
|
87
|
+
this.useDevicePassphraseState = false;
|
|
88
|
+
this.skipForceUpdateCheck = true;
|
|
89
|
+
this.params = normalizeParams(this.payload as unknown as Record<string, unknown>);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async run() {
|
|
93
|
+
const { message } = await this.device.commands.typedCall(
|
|
94
|
+
'DeviceSessionOpen',
|
|
95
|
+
'DeviceSession',
|
|
96
|
+
this.params
|
|
97
|
+
);
|
|
98
|
+
return message;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -26,7 +26,7 @@ const SUPPORTED_PAGES = new Set<number>([
|
|
|
26
26
|
function normalizePage(value: DeviceSettingsPageShowParams['page']): SupportedDeviceSettingsPage {
|
|
27
27
|
const page = typeof value === 'string' ? DeviceSettingsPage[value] : value;
|
|
28
28
|
if (page !== undefined && SUPPORTED_PAGES.has(page)) {
|
|
29
|
-
return page
|
|
29
|
+
return page;
|
|
30
30
|
}
|
|
31
31
|
throw invalidParameter(
|
|
32
32
|
'Parameter [page] must be DeviceReset, DevicePinChange, DevicePassphrase, or DeviceAirgap.'
|
|
@@ -46,6 +46,14 @@ export default class DeviceSettingsPageShow extends BaseMethod<{
|
|
|
46
46
|
page: normalizePage(this.payload.page),
|
|
47
47
|
field_name: this.payload.field_name ?? this.payload.fieldName,
|
|
48
48
|
};
|
|
49
|
+
this.protocolV2UiInteraction = {
|
|
50
|
+
request: 'button',
|
|
51
|
+
source: 'method-lifecycle',
|
|
52
|
+
reason: 'settings-page',
|
|
53
|
+
completion: 'operation-completed',
|
|
54
|
+
deviceOnly: true,
|
|
55
|
+
page: this.params.page,
|
|
56
|
+
};
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
async run() {
|
|
@@ -11,7 +11,7 @@ export default class DeviceSettingsSet extends BaseMethod<{
|
|
|
11
11
|
settings: Omit<DeviceSettings, 'passphrase_enable' | 'airgap_mode'>;
|
|
12
12
|
}> {
|
|
13
13
|
init() {
|
|
14
|
-
const settings = this.payload
|
|
14
|
+
const { settings } = this.payload;
|
|
15
15
|
if (!settings || typeof settings !== 'object' || Array.isArray(settings)) {
|
|
16
16
|
throw invalidParameter('Parameter [settings] must be an object.');
|
|
17
17
|
}
|
|
@@ -30,6 +30,16 @@ export default class DeviceSettingsSet extends BaseMethod<{
|
|
|
30
30
|
this.skipForceUpdateCheck = true;
|
|
31
31
|
this.useDevicePassphraseState = false;
|
|
32
32
|
this.params = { settings: supported };
|
|
33
|
+
if (typeof supported.label === 'string') {
|
|
34
|
+
this.protocolV2UiInteraction = {
|
|
35
|
+
request: 'button',
|
|
36
|
+
source: 'method-lifecycle',
|
|
37
|
+
reason: 'device-management',
|
|
38
|
+
completion: 'operation-completed',
|
|
39
|
+
deviceOnly: true,
|
|
40
|
+
operation: 'change-label',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
async run() {
|
|
@@ -27,7 +27,7 @@ export type DeviceUploadWallpaperResponse = {
|
|
|
27
27
|
message?: string;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
const WALLPAPER_DIRECTORY = '
|
|
30
|
+
const WALLPAPER_DIRECTORY = 'vol1:/wallpapers';
|
|
31
31
|
const SAFE_FILE_NAME = /^[A-Za-z0-9_-]+(?:\.bin)?$/;
|
|
32
32
|
|
|
33
33
|
function normalizeFileName(fileName: string | undefined, data: Uint8Array): string {
|
|
@@ -100,6 +100,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
100
100
|
data: encoded.data,
|
|
101
101
|
totalSize: encoded.data.byteLength,
|
|
102
102
|
chunkSize: this.params.chunkSize,
|
|
103
|
+
maxChunkRetries: 3,
|
|
103
104
|
overwrite: true,
|
|
104
105
|
append: false,
|
|
105
106
|
throwIfAborted: () => this.throwIfAborted(),
|
|
@@ -7,6 +7,7 @@ export type HardwareUiEventHandlers = {
|
|
|
7
7
|
button: (...event: DeviceEvents[typeof DEVICE.BUTTON]) => void;
|
|
8
8
|
passphrase: (...event: DeviceEvents[typeof DEVICE.PASSPHRASE]) => void;
|
|
9
9
|
passphraseOnDevice: (...event: DeviceEvents[typeof DEVICE.PASSPHRASE_ON_DEVICE]) => void;
|
|
10
|
+
attachPinOnDevice: (...event: DeviceEvents[typeof DEVICE.ATTACH_PIN_ON_DEVICE]) => void;
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
export function registerHardwareUiEventListeners(
|
|
@@ -17,5 +18,6 @@ export function registerHardwareUiEventListeners(
|
|
|
17
18
|
device.on(DEVICE.BUTTON, handlers.button);
|
|
18
19
|
device.on(DEVICE.PASSPHRASE, handlers.passphrase);
|
|
19
20
|
device.on(DEVICE.PASSPHRASE_ON_DEVICE, handlers.passphraseOnDevice);
|
|
21
|
+
device.on(DEVICE.ATTACH_PIN_ON_DEVICE, handlers.attachPinOnDevice);
|
|
20
22
|
return true;
|
|
21
23
|
}
|
package/src/core/index.ts
CHANGED
|
@@ -58,6 +58,10 @@ import RequestQueue from './RequestQueue';
|
|
|
58
58
|
import { registerHardwareUiEventListeners } from './deviceEventRegistration';
|
|
59
59
|
import { getSynchronize } from '../utils/getSynchronize';
|
|
60
60
|
import { runMethodWithUnlockRetry } from '../protocols/protocol-v2/unlockRetry';
|
|
61
|
+
import {
|
|
62
|
+
ProtocolV2UiInteractionCoordinator,
|
|
63
|
+
isProtocolV2UiEnabled,
|
|
64
|
+
} from '../protocols/protocol-v2/uiInteraction';
|
|
61
65
|
|
|
62
66
|
import type { ConnectSettings, Features, KnownDevice } from '../types';
|
|
63
67
|
import type { CoreMessage, IFrameCallMessage, UiPromise, UiPromiseResponse } from '../events';
|
|
@@ -403,12 +407,15 @@ const onCallDevice = async (
|
|
|
403
407
|
? onEmptyPassphraseHandler
|
|
404
408
|
: onDevicePassphraseHandler,
|
|
405
409
|
passphraseOnDevice: onEnterPassphraseOnDeviceHandler,
|
|
410
|
+
attachPinOnDevice: onEnterAttachPinOnDeviceHandler,
|
|
406
411
|
});
|
|
407
412
|
device.on(DEVICE.FEATURES, onDeviceFeaturesHandler);
|
|
408
413
|
device.on(
|
|
409
414
|
DEVICE.SELECT_DEVICE_IN_BOOTLOADER_FOR_WEB_DEVICE,
|
|
410
415
|
onSelectDeviceInBootloaderForWebDeviceHandler
|
|
411
416
|
);
|
|
417
|
+
|
|
418
|
+
const protocolV2UiCoordinator = new ProtocolV2UiInteractionCoordinator(device, postMessage);
|
|
412
419
|
device.on(
|
|
413
420
|
DEVICE.SELECT_DEVICE_FOR_SWITCH_FIRMWARE_WEB_DEVICE,
|
|
414
421
|
onSelectDeviceForSwitchFirmwareWebDeviceHandler
|
|
@@ -619,7 +626,11 @@ const onCallDevice = async (
|
|
|
619
626
|
method.device?.commands?.checkDisposed();
|
|
620
627
|
|
|
621
628
|
try {
|
|
622
|
-
const response: object = await runMethodWithUnlockRetry(
|
|
629
|
+
const response: object = await runMethodWithUnlockRetry(
|
|
630
|
+
method,
|
|
631
|
+
device,
|
|
632
|
+
protocolV2UiCoordinator
|
|
633
|
+
);
|
|
623
634
|
messageResponse = createResponseMessage(method.responseID, true, response);
|
|
624
635
|
requestQueue.resolveRequest(method.responseID, messageResponse);
|
|
625
636
|
completeMethodRequestContext(method);
|
|
@@ -636,6 +647,8 @@ const onCallDevice = async (
|
|
|
636
647
|
) {
|
|
637
648
|
throw error;
|
|
638
649
|
}
|
|
650
|
+
} finally {
|
|
651
|
+
protocolV2UiCoordinator.close();
|
|
639
652
|
}
|
|
640
653
|
};
|
|
641
654
|
Log.debug('Call API - Device Run: ', device.mainId);
|
|
@@ -689,7 +702,9 @@ const onCallDevice = async (
|
|
|
689
702
|
|
|
690
703
|
requestQueue.releaseTask(method.responseID);
|
|
691
704
|
|
|
692
|
-
|
|
705
|
+
if (isProtocolV2UiEnabled(method)) {
|
|
706
|
+
closePopup();
|
|
707
|
+
}
|
|
693
708
|
|
|
694
709
|
cleanup();
|
|
695
710
|
|
|
@@ -1235,6 +1250,9 @@ const onDevicePassphraseHandler = async (
|
|
|
1235
1250
|
device: device.toMessageObject() as KnownDevice,
|
|
1236
1251
|
passphraseState: device.passphraseState,
|
|
1237
1252
|
existsAttachPinUser: requestPayload.existsAttachPinUser,
|
|
1253
|
+
source: requestPayload.source,
|
|
1254
|
+
reason: requestPayload.reason,
|
|
1255
|
+
expectedPassphraseState: requestPayload.expectedPassphraseState,
|
|
1238
1256
|
})
|
|
1239
1257
|
);
|
|
1240
1258
|
// wait for passphrase
|
|
@@ -1256,12 +1274,27 @@ const onEmptyPassphraseHandler = (...[_, , callback]: DeviceEvents['passphrase']
|
|
|
1256
1274
|
};
|
|
1257
1275
|
|
|
1258
1276
|
const onEnterPassphraseOnDeviceHandler = (
|
|
1259
|
-
...[device]: [...DeviceEvents['passphrase_on_device']]
|
|
1277
|
+
...[device, requestPayload]: [...DeviceEvents['passphrase_on_device']]
|
|
1260
1278
|
) => {
|
|
1261
1279
|
postMessage(
|
|
1262
1280
|
createUiMessage(UI_REQUEST.REQUEST_PASSPHRASE_ON_DEVICE, {
|
|
1263
1281
|
device: device.toMessageObject() as KnownDevice,
|
|
1264
1282
|
passphraseState: device.passphraseState,
|
|
1283
|
+
source: requestPayload?.source,
|
|
1284
|
+
reason: requestPayload?.reason,
|
|
1285
|
+
})
|
|
1286
|
+
);
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
const onEnterAttachPinOnDeviceHandler = (
|
|
1290
|
+
...[device, requestPayload]: [...DeviceEvents['attach_pin_on_device']]
|
|
1291
|
+
) => {
|
|
1292
|
+
postMessage(
|
|
1293
|
+
createUiMessage(UI_REQUEST.REQUEST_PIN, {
|
|
1294
|
+
device: device.toMessageObject() as KnownDevice,
|
|
1295
|
+
type: 'ButtonRequest_AttachPin',
|
|
1296
|
+
source: requestPayload?.source,
|
|
1297
|
+
reason: requestPayload?.reason,
|
|
1265
1298
|
})
|
|
1266
1299
|
);
|
|
1267
1300
|
};
|