@onekeyfe/hd-core 1.2.2-alpha.0 → 1.2.2-alpha.100
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__/AllNetworkGetAddressBase.tracing.test.ts +4 -20
- package/__tests__/DeviceCommands.test.ts +0 -13
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +2 -0
- package/__tests__/check-firmware-release-protocol-v2.test.ts +2 -0
- package/__tests__/device-lifecycle-events.test.ts +0 -19
- package/__tests__/deviceUploadNft.test.ts +0 -41
- package/__tests__/evmSignTypedData.test.ts +0 -42
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +12 -524
- package/__tests__/open-wallet-session.test.ts +1 -127
- package/__tests__/protocol-v2-resources.test.ts +4 -7
- package/__tests__/protocol-v2-unlock-policy.test.ts +1 -35
- package/__tests__/protocol-v2.test.ts +27 -673
- package/dist/api/FirmwareUpdateV4.d.ts +0 -3
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +2 -2
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/core/index.d.ts +0 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +1 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +1 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +0 -3
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +5 -10
- package/dist/index.js +398 -1116
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +0 -2
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +0 -3
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/settings.d.ts +2 -2
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +0 -3
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +75 -416
- package/src/api/OpenWalletSession.ts +9 -39
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +1 -2
- package/src/api/evm/EVMSignTypedData.ts +0 -1
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +3 -9
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +1 -7
- package/src/api/protocol-v2/DeviceUploadNft.ts +8 -28
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +8 -19
- package/src/api/protocol-v2/helpers.ts +0 -8
- package/src/core/index.ts +10 -121
- package/src/data/messages/messages-protocol-v2.json +1 -111
- package/src/device/Device.ts +1 -3
- package/src/device/DeviceConnector.ts +3 -7
- package/src/events/ui-request.ts +0 -3
- package/src/protocols/protocol-v2/resources.ts +1 -9
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +1 -8
- package/src/protocols/protocol-v2/walletSession.ts +22 -65
- package/src/types/settings.ts +2 -4
- package/src/utils/deviceFeaturesUtils.ts +0 -4
- package/__tests__/pro2HostAssetPackage.test.ts +0 -58
- package/dist/utils/pro2HostAssetPackage.d.ts +0 -8
- package/dist/utils/pro2HostAssetPackage.d.ts.map +0 -1
- package/src/utils/pro2HostAssetPackage.ts +0 -315
|
@@ -29,12 +29,6 @@ const wasResumed = (session: unknown) =>
|
|
|
29
29
|
'resumed' in session &&
|
|
30
30
|
(session as { resumed?: unknown }).resumed === true;
|
|
31
31
|
|
|
32
|
-
const wasWalletStatusRefreshed = (session: unknown) =>
|
|
33
|
-
!!session &&
|
|
34
|
-
typeof session === 'object' &&
|
|
35
|
-
'walletStatusRefreshed' in session &&
|
|
36
|
-
(session as { walletStatusRefreshed?: unknown }).walletStatusRefreshed === true;
|
|
37
|
-
|
|
38
32
|
const requireHiddenWalletResponse = (session: { passphraseState?: string }) => {
|
|
39
33
|
if (!session.passphraseState) {
|
|
40
34
|
throw ERRORS.TypedError(
|
|
@@ -93,20 +87,12 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
93
87
|
this.unlockPolicy = 'unlock-before-run';
|
|
94
88
|
this.skipForceUpdateCheck = true;
|
|
95
89
|
this.params = normalizeParams(this.payload as unknown as Record<string, unknown>);
|
|
96
|
-
this.protocolV2PreUnlockPinType =
|
|
97
|
-
this.params.mode === OpenWalletSessionMode.SelectHidden
|
|
98
|
-
? DeviceSessionPinType.Any
|
|
99
|
-
: DeviceSessionPinType.Main;
|
|
100
90
|
this.payload.useEmptyPassphrase = this.params.mode === OpenWalletSessionMode.Standard;
|
|
101
91
|
}
|
|
102
92
|
|
|
103
93
|
async run(): Promise<OpenWalletSessionPayload> {
|
|
104
94
|
const isProtocolV2 = this.device.isProtocolV2();
|
|
105
|
-
|
|
106
|
-
isProtocolV2 && this.protocolV2UnlockContext?.preflightStatusRefreshed === true;
|
|
107
|
-
let state = reusePreflightStatus
|
|
108
|
-
? await this.device.getDeviceState()
|
|
109
|
-
: await this.device.getDeviceState({ refreshSections: ['status'] });
|
|
95
|
+
let state = await this.device.getDeviceState({ refreshSections: ['status'] });
|
|
110
96
|
let currentDeviceId = state.identity.deviceId;
|
|
111
97
|
const hasAuthoritativeProtocolV2WalletStatus = (candidate: typeof state) =>
|
|
112
98
|
candidate.status.unlocked === true &&
|
|
@@ -129,25 +115,14 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
129
115
|
currentDeviceId = state.identity.deviceId;
|
|
130
116
|
return state;
|
|
131
117
|
};
|
|
132
|
-
const resolveProtocolV2DeviceStateAfterSession = async (session: unknown) => {
|
|
133
|
-
if (!wasWalletStatusRefreshed(session)) {
|
|
134
|
-
return refreshProtocolV2DeviceState();
|
|
135
|
-
}
|
|
136
|
-
state = await this.device.getDeviceState();
|
|
137
|
-
currentDeviceId = state.identity.deviceId;
|
|
138
|
-
return state;
|
|
139
|
-
};
|
|
140
118
|
const ensureProtocolV2WalletStatus = async () => {
|
|
141
119
|
if (isProtocolV2 && !hasAuthoritativeProtocolV2WalletStatus(state)) {
|
|
142
|
-
await this.device.unlockDevice(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
method: 'openWalletSession',
|
|
149
|
-
}
|
|
150
|
-
);
|
|
120
|
+
await this.device.unlockDevice(DeviceSessionPinType.Main, {
|
|
121
|
+
source: 'unlock-coordinator',
|
|
122
|
+
reason: 'device-locked',
|
|
123
|
+
deviceOnly: true,
|
|
124
|
+
method: 'openWalletSession',
|
|
125
|
+
});
|
|
151
126
|
requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState());
|
|
152
127
|
}
|
|
153
128
|
return state;
|
|
@@ -164,16 +139,13 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
164
139
|
selectMainWalletBeforeRestore:
|
|
165
140
|
!hasAuthoritativeProtocolV2WalletStatus(state) ||
|
|
166
141
|
state.status.unlockedAttachPin === true,
|
|
167
|
-
mainPinSelected: this.protocolV2UnlockContext?.preflightMainPinSelected,
|
|
168
142
|
})
|
|
169
143
|
: await getPassphraseStateWithRefreshDeviceInfo(this.device, {
|
|
170
144
|
onlyMainPin: true,
|
|
171
145
|
initSession: this.payload.initSession,
|
|
172
146
|
});
|
|
173
147
|
const refreshedState = isProtocolV2
|
|
174
|
-
? requireAuthoritativeProtocolV2WalletStatus(
|
|
175
|
-
await resolveProtocolV2DeviceStateAfterSession(session)
|
|
176
|
-
)
|
|
148
|
+
? requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState())
|
|
177
149
|
: state;
|
|
178
150
|
const deviceId = requireDeviceId();
|
|
179
151
|
if (
|
|
@@ -261,9 +233,7 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
261
233
|
})
|
|
262
234
|
: await getPassphraseStateWithRefreshDeviceInfo(this.device, { initSession: true });
|
|
263
235
|
const refreshedState = isProtocolV2
|
|
264
|
-
? requireAuthoritativeProtocolV2WalletStatus(
|
|
265
|
-
await resolveProtocolV2DeviceStateAfterSession(session)
|
|
266
|
-
)
|
|
236
|
+
? requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState())
|
|
267
237
|
: state;
|
|
268
238
|
const deviceId = requireDeviceId();
|
|
269
239
|
if (isProtocolV2 && refreshedState.status.passphraseProtection !== true) {
|
|
@@ -400,8 +400,7 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
|
|
|
400
400
|
this.payload.passphraseState,
|
|
401
401
|
useEmptyPassphrase,
|
|
402
402
|
this.payload.skipPassphraseCheck,
|
|
403
|
-
deriveCardano
|
|
404
|
-
this.protocolV2UnlockContext?.preflightMainPinSelected
|
|
403
|
+
deriveCardano
|
|
405
404
|
);
|
|
406
405
|
if (!passphraseStateSafety) {
|
|
407
406
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
|
|
@@ -360,7 +360,6 @@ export default class EVMSignTypedData extends BaseMethod<EVMSignTypedDataParams>
|
|
|
360
360
|
|
|
361
361
|
const supportBiggerData =
|
|
362
362
|
DeviceModelToTypes.model_classic1s.includes(currentDeviceType) ||
|
|
363
|
-
DeviceModelToTypes.model_pro2.includes(currentDeviceType) ||
|
|
364
363
|
(DeviceModelToTypes.model_touch.includes(currentDeviceType) &&
|
|
365
364
|
semver.gte(currentVersion, supportBiggerDataVersion));
|
|
366
365
|
|
|
@@ -41,15 +41,9 @@ const FIRMWARE_UPDATE_CONFIRM = 'Firmware install confirmed';
|
|
|
41
41
|
*/
|
|
42
42
|
export const BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS = 5000;
|
|
43
43
|
|
|
44
|
-
type
|
|
44
|
+
type FirmwareTransferMetrics = Pick<
|
|
45
45
|
FirmwareProgress['payload'],
|
|
46
|
-
| '
|
|
47
|
-
| 'totalBytes'
|
|
48
|
-
| 'rateBytesPerSecond'
|
|
49
|
-
| 'elapsedMs'
|
|
50
|
-
| 'installTargetId'
|
|
51
|
-
| 'installPhase'
|
|
52
|
-
| 'installPhaseProgress'
|
|
46
|
+
'transferredBytes' | 'totalBytes' | 'rateBytesPerSecond' | 'elapsedMs'
|
|
53
47
|
>;
|
|
54
48
|
|
|
55
49
|
const isDeviceDisconnectedError = (error: unknown) => {
|
|
@@ -109,7 +103,7 @@ export class FirmwareUpdateBaseMethod<Params> extends BaseMethod<Params> {
|
|
|
109
103
|
postProgressMessage = (
|
|
110
104
|
progress: number,
|
|
111
105
|
progressType: IFirmwareUpdateProgressType,
|
|
112
|
-
metrics?:
|
|
106
|
+
metrics?: FirmwareTransferMetrics
|
|
113
107
|
) => {
|
|
114
108
|
this.postMessage(
|
|
115
109
|
createUiMessage(UI_REQUEST.FIRMWARE_PROGRESS, {
|
|
@@ -4,13 +4,7 @@ import { invalidParameter } from '../helpers/filesystemValidation';
|
|
|
4
4
|
import type { DeviceFirmwareUpdateStatusGetParams } from './helpers';
|
|
5
5
|
import type { DeviceFirmwareUpdateRecordFields } from '@onekeyfe/hd-transport';
|
|
6
6
|
|
|
7
|
-
const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = [
|
|
8
|
-
'status',
|
|
9
|
-
'progress_percent',
|
|
10
|
-
'phase_info',
|
|
11
|
-
'payload_version',
|
|
12
|
-
'path',
|
|
13
|
-
] as const;
|
|
7
|
+
const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = ['status', 'payload_version', 'path'] as const;
|
|
14
8
|
|
|
15
9
|
function normalizeStatusFields(
|
|
16
10
|
fields: DeviceFirmwareUpdateStatusGetParams['fields']
|
|
@@ -20,10 +20,6 @@ import {
|
|
|
20
20
|
getCompletePro2NftBasenames,
|
|
21
21
|
} from '../../utils/pro2Nft';
|
|
22
22
|
import { encodePro2Image } from '../../utils/pro2Wallpaper';
|
|
23
|
-
import {
|
|
24
|
-
buildPro2HostAssetPackage,
|
|
25
|
-
supportsPro2HostAssetPackage,
|
|
26
|
-
} from '../../utils/pro2HostAssetPackage';
|
|
27
23
|
import { BaseMethod } from '../BaseMethod';
|
|
28
24
|
import { decodeJpegBase64ToRgba } from '../helpers/base64Data';
|
|
29
25
|
import { invalidParameter } from '../helpers/filesystemValidation';
|
|
@@ -142,7 +138,7 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
|
|
|
142
138
|
this.useDevicePassphraseState = false;
|
|
143
139
|
}
|
|
144
140
|
|
|
145
|
-
private async assertCapabilities(
|
|
141
|
+
private async assertCapabilities() {
|
|
146
142
|
const protocolInfo = await this.device.ensureProtocolV2RuntimeContext();
|
|
147
143
|
const hasFileWrite = supportsProtocolV2Message(
|
|
148
144
|
protocolInfo,
|
|
@@ -154,7 +150,7 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
|
|
|
154
150
|
);
|
|
155
151
|
const hasDirList = supportsProtocolV2Message(protocolInfo, FILESYSTEM_DIR_LIST_MESSAGE_TYPE);
|
|
156
152
|
const hasNftUpdate = supportsProtocolV2Message(protocolInfo, NFT_UPDATE_MESSAGE_TYPE);
|
|
157
|
-
if (!hasFileWrite || !
|
|
153
|
+
if (!hasFileWrite || !hasPathInfo || !hasDirList || !hasNftUpdate) {
|
|
158
154
|
throw createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
|
|
159
155
|
}
|
|
160
156
|
}
|
|
@@ -196,32 +192,16 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
|
|
|
196
192
|
const { bundle } = this;
|
|
197
193
|
if (!bundle) throw invalidParameter('NFT data has not been initialized.');
|
|
198
194
|
|
|
199
|
-
|
|
200
|
-
this.device.getCurrentFirmwareVersionString()
|
|
201
|
-
);
|
|
202
|
-
await this.assertCapabilities(useHostAssetPackage);
|
|
195
|
+
await this.assertCapabilities();
|
|
203
196
|
this.throwIfAborted();
|
|
204
|
-
|
|
197
|
+
await this.assertStorageCapacity(bundle.basename);
|
|
205
198
|
this.throwIfAborted();
|
|
206
199
|
|
|
207
|
-
const
|
|
200
|
+
const files = [
|
|
208
201
|
{ path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.bin`, data: bundle.image },
|
|
209
202
|
{ path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}_m.bin`, data: bundle.thumbnail },
|
|
210
203
|
{ path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.json`, data: bundle.metadata },
|
|
211
204
|
];
|
|
212
|
-
const files = useHostAssetPackage
|
|
213
|
-
? [
|
|
214
|
-
{
|
|
215
|
-
path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.okpkg`,
|
|
216
|
-
data: buildPro2HostAssetPackage(
|
|
217
|
-
extractedFiles.map(file => ({
|
|
218
|
-
name: file.path.slice(`${PRO2_NFT_DIRECTORY}/`.length),
|
|
219
|
-
data: file.data,
|
|
220
|
-
}))
|
|
221
|
-
),
|
|
222
|
-
},
|
|
223
|
-
]
|
|
224
|
-
: extractedFiles;
|
|
225
205
|
const totalSize = files.reduce((sum, file) => sum + file.data.byteLength, 0);
|
|
226
206
|
let transferredBeforeFile = 0;
|
|
227
207
|
|
|
@@ -258,9 +238,9 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
|
|
|
258
238
|
const response = await this.updateNft(bundle.basename);
|
|
259
239
|
return {
|
|
260
240
|
basename: bundle.basename,
|
|
261
|
-
imagePath:
|
|
262
|
-
thumbnailPath:
|
|
263
|
-
metadataPath:
|
|
241
|
+
imagePath: files[0].path,
|
|
242
|
+
thumbnailPath: files[1].path,
|
|
243
|
+
metadataPath: files[2].path,
|
|
264
244
|
totalSize,
|
|
265
245
|
nftUpdated: true,
|
|
266
246
|
message: response.message?.message,
|
|
@@ -16,10 +16,6 @@ import {
|
|
|
16
16
|
type Pro2WallpaperColorFormat,
|
|
17
17
|
encodePro2Wallpaper,
|
|
18
18
|
} from '../../utils/pro2Wallpaper';
|
|
19
|
-
import {
|
|
20
|
-
buildPro2HostAssetPackage,
|
|
21
|
-
supportsPro2HostAssetPackage,
|
|
22
|
-
} from '../../utils/pro2HostAssetPackage';
|
|
23
19
|
|
|
24
20
|
export type DeviceUploadWallpaperParams = {
|
|
25
21
|
jpegBase64: string;
|
|
@@ -35,8 +31,6 @@ export type DeviceUploadWallpaperResponse = {
|
|
|
35
31
|
};
|
|
36
32
|
|
|
37
33
|
const WALLPAPER_DIRECTORY = 'vol1:/wallpapers';
|
|
38
|
-
const WALLPAPER_PACKAGE_PATH = `${WALLPAPER_DIRECTORY}/wallpaper.okpkg`;
|
|
39
|
-
const WALLPAPER_PACKAGE_ENTRY = 'wallpaper.bin';
|
|
40
34
|
const SAFE_FILE_NAME = /^[A-Za-z0-9_-]+(?:\.bin)?$/;
|
|
41
35
|
const DEVICE_SETTINGS_SET_MESSAGE_TYPE = 60412;
|
|
42
36
|
const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
|
|
@@ -122,14 +116,16 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
122
116
|
this.directoryReady = true;
|
|
123
117
|
}
|
|
124
118
|
|
|
125
|
-
private async upload(
|
|
119
|
+
private async upload() {
|
|
126
120
|
if (this.uploaded) return;
|
|
121
|
+
const { encoded } = this;
|
|
122
|
+
if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
|
|
127
123
|
|
|
128
124
|
await writeProtocolV2File({
|
|
129
125
|
commands: this.device.commands,
|
|
130
|
-
path,
|
|
131
|
-
data,
|
|
132
|
-
totalSize: data.byteLength,
|
|
126
|
+
path: this.path,
|
|
127
|
+
data: encoded.data,
|
|
128
|
+
totalSize: encoded.data.byteLength,
|
|
133
129
|
chunkSize: this.params.chunkSize,
|
|
134
130
|
maxChunkRetries: 3,
|
|
135
131
|
overwrite: true,
|
|
@@ -149,14 +145,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
149
145
|
if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
|
|
150
146
|
await this.assertCapabilities();
|
|
151
147
|
await this.ensureDirectory();
|
|
152
|
-
|
|
153
|
-
this.device.getCurrentFirmwareVersionString()
|
|
154
|
-
);
|
|
155
|
-
const data = useHostAssetPackage
|
|
156
|
-
? buildPro2HostAssetPackage([{ name: WALLPAPER_PACKAGE_ENTRY, data: encoded.data }])
|
|
157
|
-
: encoded.data;
|
|
158
|
-
if (useHostAssetPackage) this.path = WALLPAPER_PACKAGE_PATH;
|
|
159
|
-
await this.upload(this.path, data);
|
|
148
|
+
await this.upload();
|
|
160
149
|
const response = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
|
|
161
150
|
settings: { wallpaper_path: this.path },
|
|
162
151
|
});
|
|
@@ -171,7 +160,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
171
160
|
}
|
|
172
161
|
return {
|
|
173
162
|
path: this.path,
|
|
174
|
-
size: data.byteLength,
|
|
163
|
+
size: encoded.data.byteLength,
|
|
175
164
|
colorFormat: encoded.colorFormat,
|
|
176
165
|
message: response.message?.message,
|
|
177
166
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DeviceRebootType } from '@onekeyfe/hd-transport';
|
|
2
|
-
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
2
|
|
|
4
3
|
import { invalidParameter, validateNonEmptyString } from '../helpers/filesystemValidation';
|
|
5
4
|
import { ProtocolV2FirmwareTargetType } from '../../protocols/protocol-v2/firmware';
|
|
@@ -104,13 +103,6 @@ export const getProtocolV2UnknownErrorText = (error: unknown) => {
|
|
|
104
103
|
};
|
|
105
104
|
|
|
106
105
|
export const isProtocolV2DeviceDisconnectedError = (error: unknown) => {
|
|
107
|
-
const errorCode =
|
|
108
|
-
error && typeof error === 'object'
|
|
109
|
-
? (error as { errorCode?: number; code?: number }).errorCode ??
|
|
110
|
-
(error as { errorCode?: number; code?: number }).code
|
|
111
|
-
: undefined;
|
|
112
|
-
if (errorCode === HardwareErrorCode.BleDeviceDisconnected) return true;
|
|
113
|
-
|
|
114
106
|
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
115
107
|
const compactMessage = message.replace(/\s+/g, '');
|
|
116
108
|
return (
|
package/src/core/index.ts
CHANGED
|
@@ -415,13 +415,6 @@ const onCallDevice = async (
|
|
|
415
415
|
if (method.payload?.onlyConnectBleDevice) {
|
|
416
416
|
preWarmCallbackTask?.resolve();
|
|
417
417
|
Log.debug('Call API - only connect ble device: ', device?.mainId);
|
|
418
|
-
// This early return bypasses the normal-path bookkeeping at the end of the
|
|
419
|
-
// call. Without it the task leaks and haunts every later queue snapshot
|
|
420
|
-
// and cancel sweep (field log: a completed task lingered for 6 minutes),
|
|
421
|
-
// and the request stays in the active maps, so repeated preconnects pile
|
|
422
|
-
// up phantom work in diagnostics.
|
|
423
|
-
completeMethodRequestContext(method);
|
|
424
|
-
requestQueue.releaseTask(method.responseID);
|
|
425
418
|
return createResponseMessage(method.responseID, true, null);
|
|
426
419
|
}
|
|
427
420
|
|
|
@@ -651,8 +644,7 @@ const onCallDevice = async (
|
|
|
651
644
|
method.payload?.passphraseState,
|
|
652
645
|
method.payload?.useEmptyPassphrase,
|
|
653
646
|
method.payload?.skipPassphraseCheck,
|
|
654
|
-
hasDeriveCardano(method)
|
|
655
|
-
method.protocolV2UnlockContext?.preflightMainPinSelected
|
|
647
|
+
hasDeriveCardano(method)
|
|
656
648
|
);
|
|
657
649
|
|
|
658
650
|
// Double check, handles the special case of Touch/Pro
|
|
@@ -958,72 +950,11 @@ export function isMissingDetectedProtocolV2Error(method: BaseMethod, error: unkn
|
|
|
958
950
|
);
|
|
959
951
|
}
|
|
960
952
|
|
|
961
|
-
export function isProtocolV2PeerRemovedPairingError(method: BaseMethod, error: unknown) {
|
|
962
|
-
return (
|
|
963
|
-
method.payload.connectProtocol === 'V2' &&
|
|
964
|
-
(error as { errorCode?: unknown })?.errorCode ===
|
|
965
|
-
HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
|
|
969
953
|
/**
|
|
970
954
|
* If the Bluetooth connection times out, retry up to 6 times
|
|
971
955
|
* @param retryCount - Current retry count (default 0)
|
|
972
956
|
*/
|
|
973
|
-
|
|
974
|
-
// transport that never settles (field case: Electron main lost an IPC reply,
|
|
975
|
-
// "reply was never sent" after 5 minutes) hangs the call forever and cancel()
|
|
976
|
-
// only takes effect at poll checkpoints. Race acquire against a deadline and
|
|
977
|
-
// the caller's abort signal so the hang is bounded and cancel is immediate.
|
|
978
|
-
const BLE_ACQUIRE_DEADLINE_MS = 60 * 1000;
|
|
979
|
-
|
|
980
|
-
function raceBleAcquire<T>(acquirePromise: Promise<T>, abortSignal?: AbortSignal): Promise<T> {
|
|
981
|
-
return new Promise<T>((resolve, reject) => {
|
|
982
|
-
let settled = false;
|
|
983
|
-
const settle = (fn: () => void) => {
|
|
984
|
-
if (settled) return;
|
|
985
|
-
settled = true;
|
|
986
|
-
clearTimeout(deadline);
|
|
987
|
-
abortSignal?.removeEventListener('abort', onAbort);
|
|
988
|
-
fn();
|
|
989
|
-
};
|
|
990
|
-
const onAbort = () =>
|
|
991
|
-
settle(() => reject(ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled)));
|
|
992
|
-
const deadline = setTimeout(
|
|
993
|
-
() =>
|
|
994
|
-
settle(() =>
|
|
995
|
-
reject(
|
|
996
|
-
ERRORS.TypedError(
|
|
997
|
-
HardwareErrorCode.BleTimeoutError,
|
|
998
|
-
`BLE acquire exceeded ${BLE_ACQUIRE_DEADLINE_MS}ms deadline`
|
|
999
|
-
)
|
|
1000
|
-
)
|
|
1001
|
-
),
|
|
1002
|
-
BLE_ACQUIRE_DEADLINE_MS
|
|
1003
|
-
);
|
|
1004
|
-
// Attach before any early return so a late settlement of acquirePromise
|
|
1005
|
-
// is always consumed — an abort or deadline must never leave the acquire
|
|
1006
|
-
// rejection unhandled.
|
|
1007
|
-
acquirePromise.then(
|
|
1008
|
-
value => settle(() => resolve(value)),
|
|
1009
|
-
error => settle(() => reject(error))
|
|
1010
|
-
);
|
|
1011
|
-
if (abortSignal) {
|
|
1012
|
-
if (abortSignal.aborted) {
|
|
1013
|
-
onAbort();
|
|
1014
|
-
return;
|
|
1015
|
-
}
|
|
1016
|
-
abortSignal.addEventListener('abort', onAbort);
|
|
1017
|
-
}
|
|
1018
|
-
});
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
async function connectDeviceForBle(
|
|
1022
|
-
method: BaseMethod,
|
|
1023
|
-
device: Device,
|
|
1024
|
-
abortSignal?: AbortSignal,
|
|
1025
|
-
retryCount = 0
|
|
1026
|
-
) {
|
|
957
|
+
async function connectDeviceForBle(method: BaseMethod, device: Device, retryCount = 0) {
|
|
1027
958
|
try {
|
|
1028
959
|
if (device.wasInterruptedByUser()) {
|
|
1029
960
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromUser);
|
|
@@ -1037,43 +968,9 @@ async function connectDeviceForBle(
|
|
|
1037
968
|
!device.commands ||
|
|
1038
969
|
device.commands.disposed;
|
|
1039
970
|
if (shouldAcquire) {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
// legitimately block on a user-driven system bonding prompt for longer
|
|
1044
|
-
// than any sane deadline. Other envs keep the plain acquire unchanged.
|
|
1045
|
-
const useAcquireGuards = DataManager.getSettings('env') === 'desktop-web-ble';
|
|
1046
|
-
// A cancel landing during the retry backoff must not start a new acquire.
|
|
1047
|
-
if (useAcquireGuards && abortSignal?.aborted) {
|
|
1048
|
-
throw ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled);
|
|
1049
|
-
}
|
|
1050
|
-
if (!useAcquireGuards) {
|
|
1051
|
-
await device.acquire(method.payload.connectProtocol, {
|
|
1052
|
-
forceProtocolDetection: method.payload.forceProtocolDetection,
|
|
1053
|
-
});
|
|
1054
|
-
} else {
|
|
1055
|
-
try {
|
|
1056
|
-
await raceBleAcquire(
|
|
1057
|
-
device.acquire(method.payload.connectProtocol, {
|
|
1058
|
-
forceProtocolDetection: method.payload.forceProtocolDetection,
|
|
1059
|
-
}),
|
|
1060
|
-
abortSignal
|
|
1061
|
-
);
|
|
1062
|
-
} catch (err) {
|
|
1063
|
-
// A deadline hit means the transport is wedged mid-acquire; drop the
|
|
1064
|
-
// link before the retry so it cold-connects instead of stacking a
|
|
1065
|
-
// second connect onto the half-open one.
|
|
1066
|
-
if (
|
|
1067
|
-
err.errorCode === HardwareErrorCode.BleTimeoutError &&
|
|
1068
|
-
device.mainId &&
|
|
1069
|
-
device.deviceConnector
|
|
1070
|
-
) {
|
|
1071
|
-
await device.deviceConnector.disconnect(device.mainId).catch(() => undefined);
|
|
1072
|
-
device.markTransportDisconnected();
|
|
1073
|
-
}
|
|
1074
|
-
throw err;
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
971
|
+
await device.acquire(method.payload.connectProtocol, {
|
|
972
|
+
forceProtocolDetection: method.payload.forceProtocolDetection,
|
|
973
|
+
});
|
|
1077
974
|
}
|
|
1078
975
|
if (method.payload?.onlyConnectBleDevice) {
|
|
1079
976
|
if (shouldAcquire) {
|
|
@@ -1113,7 +1010,7 @@ async function connectDeviceForBle(
|
|
|
1113
1010
|
const nextRetry = retryCount + 1;
|
|
1114
1011
|
Log.debug(`Bluetooth connection will retry, retry count: ${nextRetry}`);
|
|
1115
1012
|
await wait(3000);
|
|
1116
|
-
await connectDeviceForBle(method, device,
|
|
1013
|
+
await connectDeviceForBle(method, device, nextRetry);
|
|
1117
1014
|
} else {
|
|
1118
1015
|
throw err;
|
|
1119
1016
|
}
|
|
@@ -1223,7 +1120,7 @@ const ensureConnected = async (
|
|
|
1223
1120
|
if (tryCount === 1) {
|
|
1224
1121
|
device.beginConnectionAttempt();
|
|
1225
1122
|
}
|
|
1226
|
-
await connectDeviceForBle(method, device
|
|
1123
|
+
await connectDeviceForBle(method, device);
|
|
1227
1124
|
}
|
|
1228
1125
|
resolve(device);
|
|
1229
1126
|
return;
|
|
@@ -1267,8 +1164,7 @@ const ensureConnected = async (
|
|
|
1267
1164
|
HardwareErrorCode.BridgeNeedsPermission,
|
|
1268
1165
|
HardwareErrorCode.DeviceInterruptedFromUser,
|
|
1269
1166
|
HardwareErrorCode.CallQueueActionCancelled,
|
|
1270
|
-
].includes(error.errorCode)
|
|
1271
|
-
isProtocolV2PeerRemovedPairingError(method, error)
|
|
1167
|
+
].includes(error.errorCode)
|
|
1272
1168
|
) {
|
|
1273
1169
|
reject(error);
|
|
1274
1170
|
return;
|
|
@@ -1324,9 +1220,6 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1324
1220
|
','
|
|
1325
1221
|
)}`
|
|
1326
1222
|
);
|
|
1327
|
-
// Abort before rejecting: rejectRequest releases the task and would make
|
|
1328
|
-
// its AbortController unreachable to an in-flight method loop.
|
|
1329
|
-
requestQueue.abortRequestsByConnectId(connectId);
|
|
1330
1223
|
const canceledDevices: Device[] = [];
|
|
1331
1224
|
const interruptDevice = (device: Device | undefined, deviceConnectId: string) => {
|
|
1332
1225
|
if (!device || canceledDevices.includes(device)) {
|
|
@@ -1351,6 +1244,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1351
1244
|
}
|
|
1352
1245
|
}
|
|
1353
1246
|
interruptDevice(deviceCacheMap.get(connectId), connectId);
|
|
1247
|
+
requestQueue.abortRequestsByConnectId(connectId);
|
|
1354
1248
|
pollingManager.stop(connectId);
|
|
1355
1249
|
} catch (e) {
|
|
1356
1250
|
Log.error('Cancel API Error: ', e);
|
|
@@ -1359,8 +1253,6 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1359
1253
|
const env = DataManager.getSettings('env');
|
|
1360
1254
|
if (DataManager.isBleConnect(env)) {
|
|
1361
1255
|
Log.debug('Cancel Api all _deviceList: ');
|
|
1362
|
-
// Keep method abort signals observable until every active task is rejected.
|
|
1363
|
-
requestQueue.abortAllRequests();
|
|
1364
1256
|
const canceledDevices: Device[] = [];
|
|
1365
1257
|
const interruptDevice = (device?: Device) => {
|
|
1366
1258
|
if (!device || canceledDevices.includes(device)) {
|
|
@@ -1385,6 +1277,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1385
1277
|
}
|
|
1386
1278
|
}
|
|
1387
1279
|
deviceCacheMap.forEach(interruptDevice);
|
|
1280
|
+
requestQueue.abortAllRequests();
|
|
1388
1281
|
pollingManager.stopAll();
|
|
1389
1282
|
} else {
|
|
1390
1283
|
_deviceList?.allDevices().forEach(device => {
|
|
@@ -1435,10 +1328,6 @@ const checkPassphraseEnableState = (method: BaseMethod, features?: Features) =>
|
|
|
1435
1328
|
const shouldCheckPassphraseState = (method: BaseMethod, device: Device) => {
|
|
1436
1329
|
if (!method.useDevicePassphraseState) return false;
|
|
1437
1330
|
|
|
1438
|
-
if (device.isProtocolV2() && method.payload?.useEmptyPassphrase === true) {
|
|
1439
|
-
return true;
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
1331
|
return device.hasUsePassphrase();
|
|
1443
1332
|
};
|
|
1444
1333
|
|