@onekeyfe/hd-core 1.2.2-alpha.1 → 1.2.2-alpha.10
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 +2 -2
- package/__tests__/device-lifecycle-events.test.ts +67 -0
- package/__tests__/open-wallet-session.test.ts +613 -80
- package/__tests__/pro2HostAssetPackage.test.ts +108 -1
- package/__tests__/protocol-v2.test.ts +194 -49
- package/__tests__/protocolV2FileWrite.test.ts +40 -0
- package/dist/api/FirmwareUpdateV4.d.ts +1 -0
- 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/helpers/protocolV2FileWrite.d.ts +1 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts +2 -0
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1411 -1117
- package/dist/protocols/protocol-v2/walletSession.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/pro2HostAssetPackage.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +42 -10
- package/src/api/OpenWalletSession.ts +0 -3
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +3 -1
- package/src/api/helpers/protocolV2FileWrite.ts +11 -5
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +8 -2
- package/src/core/RequestQueue.ts +16 -1
- package/src/core/index.ts +48 -21
- package/src/data/messages/messages-protocol-v2.json +1471 -1367
- package/src/protocols/protocol-v2/walletSession.ts +157 -38
- package/src/utils/pro2HostAssetPackage.ts +73 -27
|
@@ -36,6 +36,8 @@ export type ProtocolV2FileWriteOptions = {
|
|
|
36
36
|
chunkSize?: number;
|
|
37
37
|
chunkLen?: number;
|
|
38
38
|
chunkSizeLimit?: number;
|
|
39
|
+
/** BLE-only limit for a caller whose fixed short path has a verified larger frame budget. */
|
|
40
|
+
bleChunkSizeLimit?: number;
|
|
39
41
|
overwrite?: boolean;
|
|
40
42
|
append?: boolean;
|
|
41
43
|
uiPercentage?: number;
|
|
@@ -114,11 +116,15 @@ export function isProtocolV2ResponseTimeout(error: unknown) {
|
|
|
114
116
|
);
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
function getProtocolV2FileChunkLimit() {
|
|
119
|
+
function getProtocolV2FileChunkLimit(bleChunkSizeLimit?: number) {
|
|
118
120
|
const env = DataManager.getSettings('env');
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
if (env && DataManager.isBleConnect(env)) {
|
|
122
|
+
const configuredLimit = Number(bleChunkSizeLimit);
|
|
123
|
+
return Number.isFinite(configuredLimit) && configuredLimit > 0
|
|
124
|
+
? Math.floor(configuredLimit)
|
|
125
|
+
: PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
126
|
+
}
|
|
127
|
+
return PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
|
|
122
128
|
}
|
|
123
129
|
|
|
124
130
|
async function dataToUint8Array(data: ProtocolV2FileWriteData): Promise<Uint8Array> {
|
|
@@ -178,7 +184,7 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
178
184
|
);
|
|
179
185
|
}
|
|
180
186
|
|
|
181
|
-
const defaultChunkSizeLimit = getProtocolV2FileChunkLimit();
|
|
187
|
+
const defaultChunkSizeLimit = getProtocolV2FileChunkLimit(options.bleChunkSizeLimit);
|
|
182
188
|
const configuredChunkSizeLimit = Number(options.chunkSizeLimit);
|
|
183
189
|
const chunkSizeLimit =
|
|
184
190
|
Number.isFinite(configuredChunkSizeLimit) && configuredChunkSizeLimit > 0
|
|
@@ -49,6 +49,7 @@ const SAFE_FILE_NAME = /^[A-Za-z0-9_-]+(?:\.bin)?$/;
|
|
|
49
49
|
const DEVICE_SETTINGS_SET_MESSAGE_TYPE = 60412;
|
|
50
50
|
const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
|
|
51
51
|
const FILESYSTEM_DIR_MAKE_MESSAGE_TYPE = 60809;
|
|
52
|
+
const WALLPAPER_PACKAGE_BLE_CHUNK_SIZE = 1960;
|
|
52
53
|
const Log = getLogger(LoggerNames.Method);
|
|
53
54
|
|
|
54
55
|
function normalizeFileName(fileName: string | undefined, data: Uint8Array): string {
|
|
@@ -130,7 +131,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
130
131
|
this.directoryReady = true;
|
|
131
132
|
}
|
|
132
133
|
|
|
133
|
-
private async upload(path: string, data: Uint8Array) {
|
|
134
|
+
private async upload(path: string, data: Uint8Array, bleChunkSizeLimit?: number) {
|
|
134
135
|
if (this.uploaded) return;
|
|
135
136
|
|
|
136
137
|
await writeProtocolV2File({
|
|
@@ -139,6 +140,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
139
140
|
data,
|
|
140
141
|
totalSize: data.byteLength,
|
|
141
142
|
chunkSize: this.params.chunkSize,
|
|
143
|
+
bleChunkSizeLimit,
|
|
142
144
|
maxChunkRetries: 3,
|
|
143
145
|
overwrite: true,
|
|
144
146
|
append: false,
|
|
@@ -164,7 +166,11 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
164
166
|
? buildPro2HostAssetPackage([{ name: WALLPAPER_PACKAGE_ENTRY, data: encoded.data }])
|
|
165
167
|
: encoded.data;
|
|
166
168
|
if (useHostAssetPackage) this.path = WALLPAPER_PACKAGE_PATH;
|
|
167
|
-
await this.upload(
|
|
169
|
+
await this.upload(
|
|
170
|
+
this.path,
|
|
171
|
+
data,
|
|
172
|
+
useHostAssetPackage ? WALLPAPER_PACKAGE_BLE_CHUNK_SIZE : undefined
|
|
173
|
+
);
|
|
168
174
|
const response = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
|
|
169
175
|
settings: { wallpaper_path: this.path },
|
|
170
176
|
});
|
package/src/core/RequestQueue.ts
CHANGED
|
@@ -56,11 +56,20 @@ export default class RequestQueue {
|
|
|
56
56
|
return false;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
private isRequestForConnectId(request: RequestTask, connectId: string) {
|
|
60
|
+
const { method } = request;
|
|
61
|
+
return (
|
|
62
|
+
method.connectId === connectId ||
|
|
63
|
+
method.device?.mainId === connectId ||
|
|
64
|
+
method.device?.getConnectId() === connectId
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
// 取消与指定connectId相关的所有请求
|
|
60
69
|
public abortRequestsByConnectId(connectId: string) {
|
|
61
70
|
let count = 0;
|
|
62
71
|
this.requestQueue.forEach((request, _) => {
|
|
63
|
-
if (request.abortController && request
|
|
72
|
+
if (request.abortController && this.isRequestForConnectId(request, connectId)) {
|
|
64
73
|
request.abortController.abort();
|
|
65
74
|
request.abortController = undefined;
|
|
66
75
|
count++;
|
|
@@ -69,6 +78,12 @@ export default class RequestQueue {
|
|
|
69
78
|
return count;
|
|
70
79
|
}
|
|
71
80
|
|
|
81
|
+
public getRequestTasksIdByConnectId(connectId: string) {
|
|
82
|
+
return Array.from(this.requestQueue.values())
|
|
83
|
+
.filter(request => this.isRequestForConnectId(request, connectId))
|
|
84
|
+
.map(request => request.id);
|
|
85
|
+
}
|
|
86
|
+
|
|
72
87
|
// 取消所有请求
|
|
73
88
|
public abortAllRequests() {
|
|
74
89
|
let count = 0;
|
package/src/core/index.ts
CHANGED
|
@@ -2,7 +2,11 @@ import semver from 'semver';
|
|
|
2
2
|
import EventEmitter from 'events';
|
|
3
3
|
import {
|
|
4
4
|
DeviceSessionPinType,
|
|
5
|
+
type LowlevelTransportSharedPlugin,
|
|
6
|
+
type OneKeyDeviceInfo,
|
|
7
|
+
type ProtocolType,
|
|
5
8
|
TRANSPORT_EVENT,
|
|
9
|
+
type TransportDeviceDisconnectEvent,
|
|
6
10
|
isProtocolV2LinkDisabledError,
|
|
7
11
|
} from '@onekeyfe/hd-transport';
|
|
8
12
|
import {
|
|
@@ -74,11 +78,6 @@ import type { CoreMessage, IFrameCallMessage, UiPromise, UiPromiseResponse } fro
|
|
|
74
78
|
import type { DeviceEvents, InitOptions, RunOptions } from '../device/Device';
|
|
75
79
|
import type { SdkTracingContext } from '../utils/tracing';
|
|
76
80
|
import type { Deferred } from '@onekeyfe/hd-shared';
|
|
77
|
-
import type {
|
|
78
|
-
LowlevelTransportSharedPlugin,
|
|
79
|
-
OneKeyDeviceInfo,
|
|
80
|
-
TransportDeviceDisconnectEvent,
|
|
81
|
-
} from '@onekeyfe/hd-transport';
|
|
82
81
|
import type { BaseMethod } from '../api/BaseMethod';
|
|
83
82
|
|
|
84
83
|
const Log = getLogger(LoggerNames.Core);
|
|
@@ -92,7 +91,7 @@ const preWarmDoneAt = new Map<string, number>();
|
|
|
92
91
|
|
|
93
92
|
export type CoreContext = ReturnType<Core['getCoreContext']>;
|
|
94
93
|
|
|
95
|
-
function
|
|
94
|
+
function resolveDeriveCardano(method: BaseMethod): boolean | undefined {
|
|
96
95
|
if (
|
|
97
96
|
method.name.startsWith('allNetworkGetAddress') &&
|
|
98
97
|
method.payload &&
|
|
@@ -102,15 +101,22 @@ function hasDeriveCardano(method: BaseMethod): boolean {
|
|
|
102
101
|
) {
|
|
103
102
|
return true;
|
|
104
103
|
}
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
if (method.name.startsWith('cardano')) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
// V1 Initialize only sends derive_cardano on an explicit true.
|
|
108
|
+
// V2 AskPassphrase maps true to [Standard, Cardano] and anything else to [Standard].
|
|
109
|
+
if (method.payload?.deriveCardano === true) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
107
113
|
}
|
|
108
114
|
|
|
109
115
|
const parseInitOptions = (method?: BaseMethod): InitOptions => ({
|
|
110
116
|
initSession: method?.payload.initSession,
|
|
111
117
|
passphraseState: method?.payload.useEmptyPassphrase ? undefined : method?.payload.passphraseState,
|
|
112
118
|
deviceId: method?.payload.deviceId,
|
|
113
|
-
deriveCardano: method
|
|
119
|
+
deriveCardano: method ? resolveDeriveCardano(method) : undefined,
|
|
114
120
|
connectProtocol: method?.payload.connectProtocol,
|
|
115
121
|
forceProtocolDetection: method?.payload.forceProtocolDetection,
|
|
116
122
|
protocolV2DeviceInfoTimeoutMs: method?.payload.protocolV2DeviceInfoTimeoutMs,
|
|
@@ -651,7 +657,7 @@ const onCallDevice = async (
|
|
|
651
657
|
method.payload?.passphraseState,
|
|
652
658
|
method.payload?.useEmptyPassphrase,
|
|
653
659
|
method.payload?.skipPassphraseCheck,
|
|
654
|
-
|
|
660
|
+
resolveDeriveCardano(method),
|
|
655
661
|
method.protocolV2UnlockContext?.preflightMainPinSelected
|
|
656
662
|
);
|
|
657
663
|
|
|
@@ -1037,6 +1043,7 @@ async function connectDeviceForBle(
|
|
|
1037
1043
|
!device.commands ||
|
|
1038
1044
|
device.commands.disposed;
|
|
1039
1045
|
if (shouldAcquire) {
|
|
1046
|
+
const connectProtocol = resolveBleConnectProtocol(method);
|
|
1040
1047
|
// The deadline/abort guards are scoped to the desktop electron
|
|
1041
1048
|
// transport: its IPC acquire is the only path with a proven
|
|
1042
1049
|
// never-settling failure mode, while react-native/lowlevel acquire may
|
|
@@ -1048,13 +1055,13 @@ async function connectDeviceForBle(
|
|
|
1048
1055
|
throw ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled);
|
|
1049
1056
|
}
|
|
1050
1057
|
if (!useAcquireGuards) {
|
|
1051
|
-
await device.acquire(
|
|
1058
|
+
await device.acquire(connectProtocol, {
|
|
1052
1059
|
forceProtocolDetection: method.payload.forceProtocolDetection,
|
|
1053
1060
|
});
|
|
1054
1061
|
} else {
|
|
1055
1062
|
try {
|
|
1056
1063
|
await raceBleAcquire(
|
|
1057
|
-
device.acquire(
|
|
1064
|
+
device.acquire(connectProtocol, {
|
|
1058
1065
|
forceProtocolDetection: method.payload.forceProtocolDetection,
|
|
1059
1066
|
}),
|
|
1060
1067
|
abortSignal
|
|
@@ -1120,6 +1127,14 @@ async function connectDeviceForBle(
|
|
|
1120
1127
|
}
|
|
1121
1128
|
}
|
|
1122
1129
|
|
|
1130
|
+
export function resolveBleConnectProtocol(method: BaseMethod): ProtocolType | undefined {
|
|
1131
|
+
if (method.payload.connectProtocol === 'V1' || method.payload.connectProtocol === 'V2') {
|
|
1132
|
+
return method.payload.connectProtocol;
|
|
1133
|
+
}
|
|
1134
|
+
const supportedProtocols = method.getSupportedProtocols();
|
|
1135
|
+
return supportedProtocols.length === 1 && supportedProtocols[0] === 'V2' ? 'V2' : undefined;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1123
1138
|
type IPollFn<T> = (time?: number) => T;
|
|
1124
1139
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1125
1140
|
const ensureConnected = async (
|
|
@@ -1318,7 +1333,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1318
1333
|
// cancel callback tasks
|
|
1319
1334
|
requestQueue.cancelCallbackTasks(connectId);
|
|
1320
1335
|
|
|
1321
|
-
const requestIds = requestQueue.
|
|
1336
|
+
const requestIds = requestQueue.getRequestTasksIdByConnectId(connectId);
|
|
1322
1337
|
Log.debug(
|
|
1323
1338
|
`Cancel Api connect requestQueues: length:${requestIds.length} requestIds:${requestIds.join(
|
|
1324
1339
|
','
|
|
@@ -1326,6 +1341,8 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1326
1341
|
);
|
|
1327
1342
|
// Abort before rejecting: rejectRequest releases the task and would make
|
|
1328
1343
|
// its AbortController unreachable to an in-flight method loop.
|
|
1344
|
+
// Match both the requested connectId and a device selected internally by the
|
|
1345
|
+
// method, such as Desktop WebUSB firmwareUpdateV4.
|
|
1329
1346
|
requestQueue.abortRequestsByConnectId(connectId);
|
|
1330
1347
|
const canceledDevices: Device[] = [];
|
|
1331
1348
|
const interruptDevice = (device: Device | undefined, deviceConnectId: string) => {
|
|
@@ -1342,7 +1359,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1342
1359
|
// During ensureConnected the method has a connectId but device is
|
|
1343
1360
|
// assigned only after the poll succeeds. Interrupt the cached BLE
|
|
1344
1361
|
// Device so an in-flight acquire/initialize cannot finish.
|
|
1345
|
-
interruptDevice(task.method?.device,
|
|
1362
|
+
interruptDevice(task.method?.device, connectId);
|
|
1346
1363
|
interruptDevice(deviceCacheMap.get(connectId), connectId);
|
|
1347
1364
|
requestQueue.rejectRequest(
|
|
1348
1365
|
requestId,
|
|
@@ -1357,10 +1374,11 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1357
1374
|
}
|
|
1358
1375
|
} else {
|
|
1359
1376
|
const env = DataManager.getSettings('env');
|
|
1377
|
+
// Abort every method before rejecting its queue task. Non-BLE methods also
|
|
1378
|
+
// use the signal to stop recovery loops after the public promise is rejected.
|
|
1379
|
+
requestQueue.abortAllRequests();
|
|
1360
1380
|
if (DataManager.isBleConnect(env)) {
|
|
1361
1381
|
Log.debug('Cancel Api all _deviceList: ');
|
|
1362
|
-
// Keep method abort signals observable until every active task is rejected.
|
|
1363
|
-
requestQueue.abortAllRequests();
|
|
1364
1382
|
const canceledDevices: Device[] = [];
|
|
1365
1383
|
const interruptDevice = (device?: Device) => {
|
|
1366
1384
|
if (!device || canceledDevices.includes(device)) {
|
|
@@ -1403,8 +1421,10 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
1403
1421
|
}
|
|
1404
1422
|
}
|
|
1405
1423
|
|
|
1406
|
-
cleanup();
|
|
1407
|
-
|
|
1424
|
+
cleanup(connectId);
|
|
1425
|
+
if (!connectId || _uiPromises.length === 0) {
|
|
1426
|
+
closePopup();
|
|
1427
|
+
}
|
|
1408
1428
|
};
|
|
1409
1429
|
|
|
1410
1430
|
const checkPassphraseEnableState = (method: BaseMethod, features?: Features) => {
|
|
@@ -1442,9 +1462,16 @@ const shouldCheckPassphraseState = (method: BaseMethod, device: Device) => {
|
|
|
1442
1462
|
return device.hasUsePassphrase();
|
|
1443
1463
|
};
|
|
1444
1464
|
|
|
1445
|
-
const cleanup = () => {
|
|
1446
|
-
const pendingUiPromises =
|
|
1447
|
-
|
|
1465
|
+
const cleanup = (connectId?: string) => {
|
|
1466
|
+
const pendingUiPromises = connectId
|
|
1467
|
+
? _uiPromises.filter(
|
|
1468
|
+
uiPromise =>
|
|
1469
|
+
uiPromise.data?.mainId === connectId || uiPromise.data?.getConnectId() === connectId
|
|
1470
|
+
)
|
|
1471
|
+
: _uiPromises;
|
|
1472
|
+
_uiPromises = connectId
|
|
1473
|
+
? _uiPromises.filter(uiPromise => !pendingUiPromises.includes(uiPromise))
|
|
1474
|
+
: [];
|
|
1448
1475
|
rejectUiPromises(
|
|
1449
1476
|
pendingUiPromises,
|
|
1450
1477
|
ERRORS.TypedError(HardwareErrorCode.ActionCancelled, 'UI request was cancelled')
|