@onekeyfe/hd-core 1.1.4-alpha.2 → 1.1.4-alpha.3
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/dist/api/BaseMethod.d.ts +2 -0
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddress.d.ts +4 -28
- package/dist/api/allnetwork/AllNetworkGetAddress.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts +47 -0
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -0
- package/dist/api/allnetwork/AllNetworkGetAddressByLoop.d.ts +8 -0
- package/dist/api/allnetwork/AllNetworkGetAddressByLoop.d.ts.map +1 -0
- package/dist/api/btc/BTCGetPublicKey.d.ts.map +1 -1
- package/dist/api/cosmos/CosmosGetPublicKey.d.ts +1 -1
- package/dist/api/cosmos/CosmosGetPublicKey.d.ts.map +1 -1
- package/dist/api/device/DeviceUnlock.d.ts +0 -2
- package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
- package/dist/api/evm/EVMGetPublicKey.d.ts +1 -0
- package/dist/api/evm/EVMGetPublicKey.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts +4 -0
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +4 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/events/call.d.ts +9 -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 +11 -1
- package/dist/events/iframe.d.ts.map +1 -1
- package/dist/index.d.ts +41 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +408 -195
- package/dist/inject.d.ts +3 -0
- package/dist/inject.d.ts.map +1 -1
- package/dist/types/api/allNetworkGetAddress.d.ts +12 -3
- package/dist/types/api/allNetworkGetAddress.d.ts.map +1 -1
- package/dist/types/api/evmGetPublicKey.d.ts +1 -0
- package/dist/types/api/evmGetPublicKey.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +2 -1
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/utils/findDefectiveBatchDevice.d.ts +0 -5
- package/dist/utils/findDefectiveBatchDevice.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +3 -0
- package/src/api/allnetwork/AllNetworkGetAddress.ts +19 -408
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +480 -0
- package/src/api/allnetwork/AllNetworkGetAddressByLoop.ts +161 -0
- package/src/api/btc/BTCGetPublicKey.ts +13 -0
- package/src/api/cosmos/CosmosGetPublicKey.ts +1 -1
- package/src/api/device/DeviceUnlock.ts +1 -48
- package/src/api/evm/EVMGetPublicKey.ts +9 -3
- package/src/api/index.ts +1 -0
- package/src/core/RequestQueue.ts +26 -0
- package/src/core/index.ts +42 -82
- package/src/device/Device.ts +66 -0
- package/src/events/call.ts +10 -0
- package/src/events/core.ts +7 -1
- package/src/events/iframe.ts +12 -1
- package/src/index.ts +2 -1
- package/src/inject.ts +47 -0
- package/src/types/api/allNetworkGetAddress.ts +17 -3
- package/src/types/api/evmGetPublicKey.ts +1 -0
- package/src/types/api/index.ts +2 -1
- package/src/utils/findDefectiveBatchDevice.ts +0 -29
|
@@ -1,59 +1,12 @@
|
|
|
1
1
|
import { LockDevice } from '@onekeyfe/hd-transport';
|
|
2
|
-
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
|
-
import semver from 'semver';
|
|
4
2
|
import { BaseMethod } from '../BaseMethod';
|
|
5
|
-
import { toHardened } from '../helpers/pathUtils';
|
|
6
|
-
import { DeviceFirmwareRange } from '../../types';
|
|
7
|
-
import { getDeviceFirmwareVersion, getMethodVersionRange } from '../../utils';
|
|
8
3
|
|
|
9
4
|
export default class DeviceUnlock extends BaseMethod<LockDevice> {
|
|
10
5
|
init() {
|
|
11
6
|
this.useDevicePassphraseState = false;
|
|
12
7
|
}
|
|
13
8
|
|
|
14
|
-
supportUnlockVersionRange(): DeviceFirmwareRange {
|
|
15
|
-
return {
|
|
16
|
-
pro: {
|
|
17
|
-
min: '4.15.0',
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
9
|
async run() {
|
|
23
|
-
|
|
24
|
-
const versionRange = getMethodVersionRange(
|
|
25
|
-
this.device.features,
|
|
26
|
-
type => this.supportUnlockVersionRange()[type]
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
if (versionRange && semver.gte(firmwareVersion, versionRange.min)) {
|
|
30
|
-
const res = await this.device.commands.typedCall('UnLockDevice', 'UnLockDeviceResponse');
|
|
31
|
-
if (this.device.features) {
|
|
32
|
-
this.device.features.unlocked = res.message.unlocked == null ? null : res.message.unlocked;
|
|
33
|
-
this.device.features.unlocked_attach_pin =
|
|
34
|
-
res.message.unlocked_attach_pin == null ? undefined : res.message.unlocked_attach_pin;
|
|
35
|
-
this.device.features.passphrase_protection =
|
|
36
|
-
res.message.passphrase_protection == null ? null : res.message.passphrase_protection;
|
|
37
|
-
|
|
38
|
-
return Promise.resolve(this.device.features);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const featuresRes = await this.device.commands.typedCall('GetFeatures', 'Features');
|
|
42
|
-
return Promise.resolve(featuresRes.message);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const { type } = await this.device.commands.typedCall('GetAddress', 'Address', {
|
|
46
|
-
address_n: [toHardened(44), toHardened(1), toHardened(0), 0, 0],
|
|
47
|
-
coin_name: 'Testnet',
|
|
48
|
-
script_type: 'SPENDADDRESS',
|
|
49
|
-
show_display: false,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// @ts-expect-error
|
|
53
|
-
if (type === 'CallMethodError') {
|
|
54
|
-
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Get the passphrase state error');
|
|
55
|
-
}
|
|
56
|
-
const res = await this.device.commands.typedCall('GetFeatures', 'Features');
|
|
57
|
-
return Promise.resolve(res.message);
|
|
10
|
+
return this.device.unlockDevice();
|
|
58
11
|
}
|
|
59
12
|
}
|
|
@@ -13,6 +13,8 @@ import { batchGetPublickeys } from '../helpers/batchGetPublickeys';
|
|
|
13
13
|
export default class EVMGetPublicKey extends BaseMethod<EthereumGetPublicKeyOneKey[]> {
|
|
14
14
|
hasBundle = false;
|
|
15
15
|
|
|
16
|
+
confirmShowOnOneKey = false;
|
|
17
|
+
|
|
16
18
|
useBatch = false;
|
|
17
19
|
|
|
18
20
|
init() {
|
|
@@ -20,9 +22,13 @@ export default class EVMGetPublicKey extends BaseMethod<EthereumGetPublicKeyOneK
|
|
|
20
22
|
this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.NOT_INITIALIZE];
|
|
21
23
|
|
|
22
24
|
this.hasBundle = !!this.payload?.bundle;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
|
|
26
|
+
this.confirmShowOnOneKey = this.payload?.bundle?.some(
|
|
27
|
+
(item: EVMGetPublicKeyParams) => !!item.showOnOneKey
|
|
25
28
|
);
|
|
29
|
+
|
|
30
|
+
this.useBatch = !this.confirmShowOnOneKey && this.hasBundle && this.payload.useBatch;
|
|
31
|
+
|
|
26
32
|
const payload = this.hasBundle ? this.payload : { bundle: [this.payload] };
|
|
27
33
|
|
|
28
34
|
// check payload
|
|
@@ -66,7 +72,7 @@ export default class EVMGetPublicKey extends BaseMethod<EthereumGetPublicKeyOneK
|
|
|
66
72
|
async run() {
|
|
67
73
|
const responses: EVMPublicKey[] = [];
|
|
68
74
|
|
|
69
|
-
if (this.useBatch &&
|
|
75
|
+
if (this.useBatch && supportBatchPublicKey(this.device?.features)) {
|
|
70
76
|
try {
|
|
71
77
|
const res = await batchGetPublickeys(this.device, this.params, 'secp256k1', 60, {
|
|
72
78
|
includeNode: false,
|
package/src/api/index.ts
CHANGED
|
@@ -41,6 +41,7 @@ export { default as promptWebDeviceAccess } from './PromptWebDeviceAccess';
|
|
|
41
41
|
export { default as cipherKeyValue } from './CipherKeyValue';
|
|
42
42
|
|
|
43
43
|
export { default as allNetworkGetAddress } from './allnetwork/AllNetworkGetAddress';
|
|
44
|
+
export { default as allNetworkGetAddressByLoop } from './allnetwork/AllNetworkGetAddressByLoop';
|
|
44
45
|
|
|
45
46
|
export { default as btcGetAddress } from './btc/BTCGetAddress';
|
|
46
47
|
export { default as btcGetPublicKey } from './btc/BTCGetPublicKey';
|
package/src/core/RequestQueue.ts
CHANGED
|
@@ -13,6 +13,8 @@ export type RequestTask = {
|
|
|
13
13
|
export default class RequestQueue {
|
|
14
14
|
private requestQueue = new Map<number, RequestTask>();
|
|
15
15
|
|
|
16
|
+
private pendingCallbackTasks = new Map<string, Deferred<void>>();
|
|
17
|
+
|
|
16
18
|
// 生成唯一请求ID
|
|
17
19
|
public generateRequestId = (method?: BaseMethod) => {
|
|
18
20
|
if (method && method.responseID != null) {
|
|
@@ -104,4 +106,28 @@ export default class RequestQueue {
|
|
|
104
106
|
public releaseTask(requestId: number) {
|
|
105
107
|
this.requestQueue.delete(requestId);
|
|
106
108
|
}
|
|
109
|
+
|
|
110
|
+
public registerPendingCallbackTask(connectId: string, callbackPromise: Deferred<void>) {
|
|
111
|
+
this.pendingCallbackTasks.set(connectId, callbackPromise);
|
|
112
|
+
|
|
113
|
+
callbackPromise.promise.finally(() => {
|
|
114
|
+
Log.debug(`Callback task completed for connectId: ${connectId}`);
|
|
115
|
+
this.pendingCallbackTasks.delete(connectId);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public async waitForPendingCallbackTasks(connectId: string): Promise<void> {
|
|
120
|
+
const pendingTask = this.pendingCallbackTasks.get(connectId);
|
|
121
|
+
if (pendingTask) {
|
|
122
|
+
Log.debug(`Waiting for pending callback task to complete for connectId: ${connectId}`);
|
|
123
|
+
await pendingTask.promise;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public cancelCallbackTasks(connectId: string) {
|
|
128
|
+
const pendingTask = this.pendingCallbackTasks.get(connectId);
|
|
129
|
+
if (pendingTask) {
|
|
130
|
+
pendingTask.resolve();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
107
133
|
}
|
package/src/core/index.ts
CHANGED
|
@@ -3,19 +3,17 @@ import EventEmitter from 'events';
|
|
|
3
3
|
import { Features, LowlevelTransportSharedPlugin, OneKeyDeviceInfo } from '@onekeyfe/hd-transport';
|
|
4
4
|
import {
|
|
5
5
|
createDeferred,
|
|
6
|
-
Deferred,
|
|
7
|
-
ERRORS,
|
|
8
|
-
HardwareError,
|
|
9
|
-
HardwareErrorCode,
|
|
10
6
|
createDeprecatedHardwareError,
|
|
11
7
|
createNeedUpgradeFirmwareHardwareError,
|
|
12
8
|
createNewFirmwareForceUpdateHardwareError,
|
|
13
9
|
createNewFirmwareUnReleaseHardwareError,
|
|
14
|
-
|
|
10
|
+
Deferred,
|
|
11
|
+
ERRORS,
|
|
12
|
+
HardwareError,
|
|
13
|
+
HardwareErrorCode,
|
|
15
14
|
} from '@onekeyfe/hd-shared';
|
|
16
15
|
import {
|
|
17
16
|
getDeviceFirmwareVersion,
|
|
18
|
-
getDeviceBLEFirmwareVersion,
|
|
19
17
|
enableLog,
|
|
20
18
|
getLogger,
|
|
21
19
|
LoggerNames,
|
|
@@ -23,10 +21,6 @@ import {
|
|
|
23
21
|
wait,
|
|
24
22
|
getMethodVersionRange,
|
|
25
23
|
} from '../utils';
|
|
26
|
-
import {
|
|
27
|
-
findDefectiveBatchDevice,
|
|
28
|
-
getDefectiveDeviceInfo,
|
|
29
|
-
} from '../utils/findDefectiveBatchDevice';
|
|
30
24
|
import { supportNewPassphrase } from '../utils/deviceFeaturesUtils';
|
|
31
25
|
import { Device, DeviceEvents, InitOptions, RunOptions } from '../device/Device';
|
|
32
26
|
import { DeviceList } from '../device/DeviceList';
|
|
@@ -58,7 +52,7 @@ import { getSynchronize } from '../utils/getSynchronize';
|
|
|
58
52
|
|
|
59
53
|
const Log = getLogger(LoggerNames.Core);
|
|
60
54
|
|
|
61
|
-
type CoreContext = ReturnType<Core['getCoreContext']>;
|
|
55
|
+
export type CoreContext = ReturnType<Core['getCoreContext']>;
|
|
62
56
|
|
|
63
57
|
function hasDeriveCardano(method: BaseMethod): boolean {
|
|
64
58
|
if (
|
|
@@ -201,6 +195,11 @@ const onCallDevice = async (
|
|
|
201
195
|
DevicePool.clearDeviceCache(method.payload.connectId);
|
|
202
196
|
}
|
|
203
197
|
|
|
198
|
+
// wait for previous callback tasks to complete (ensure device does not call concurrently)
|
|
199
|
+
if (method.connectId) {
|
|
200
|
+
await context.waitForCallbackTasks(method.connectId);
|
|
201
|
+
}
|
|
202
|
+
|
|
204
203
|
await waitForPendingPromise(getPrePendingCallPromise, setPrePendingCallPromise);
|
|
205
204
|
|
|
206
205
|
const task = requestQueue.createTask(method);
|
|
@@ -231,6 +230,7 @@ const onCallDevice = async (
|
|
|
231
230
|
|
|
232
231
|
Log.debug('Call API - setDevice: ', device.mainId);
|
|
233
232
|
method.setDevice?.(device);
|
|
233
|
+
method.context = context;
|
|
234
234
|
|
|
235
235
|
device.on(DEVICE.PIN, onDevicePinHandler);
|
|
236
236
|
device.on(DEVICE.BUTTON, onDeviceButtonHandler);
|
|
@@ -246,6 +246,10 @@ const onCallDevice = async (
|
|
|
246
246
|
);
|
|
247
247
|
|
|
248
248
|
try {
|
|
249
|
+
if (method.connectId) {
|
|
250
|
+
await context.waitForCallbackTasks(method.connectId);
|
|
251
|
+
}
|
|
252
|
+
|
|
249
253
|
await waitForPendingPromise(getPrePendingCallPromise, setPrePendingCallPromise);
|
|
250
254
|
|
|
251
255
|
const inner = async (): Promise<void> => {
|
|
@@ -257,96 +261,35 @@ const onCallDevice = async (
|
|
|
257
261
|
|
|
258
262
|
if (device.features) {
|
|
259
263
|
await DataManager.checkAndReloadData();
|
|
260
|
-
|
|
261
|
-
// 检测故障固件设备
|
|
262
|
-
if (findDefectiveBatchDevice(device.features)) {
|
|
263
|
-
const defectiveInfo = getDefectiveDeviceInfo(device.features);
|
|
264
|
-
if (defectiveInfo) {
|
|
265
|
-
throw createDefectiveFirmwareError(
|
|
266
|
-
defectiveInfo.serialNo,
|
|
267
|
-
defectiveInfo.seVersion || 'Unknown',
|
|
268
|
-
defectiveInfo.deviceType,
|
|
269
|
-
method.connectId,
|
|
270
|
-
method.deviceId
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
264
|
const newVersionStatus = DataManager.getFirmwareStatus(device.features);
|
|
276
265
|
const bleVersionStatus = DataManager.getBLEFirmwareStatus(device.features);
|
|
277
|
-
|
|
278
|
-
const currentFirmwareVersion = getDeviceFirmwareVersion(device.features).join('.');
|
|
279
|
-
const currentBleVersion = getDeviceBLEFirmwareVersion(device.features).join('.');
|
|
280
266
|
if (
|
|
281
267
|
(newVersionStatus === 'required' || bleVersionStatus === 'required') &&
|
|
282
268
|
method.skipForceUpdateCheck === false
|
|
283
269
|
) {
|
|
284
|
-
|
|
285
|
-
const currentVersions = {
|
|
286
|
-
firmware: currentFirmwareVersion,
|
|
287
|
-
ble: currentBleVersion,
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
// Provide more specific error message based on which version check failed
|
|
291
|
-
if (newVersionStatus === 'required' && bleVersionStatus === 'required') {
|
|
292
|
-
throw createNewFirmwareForceUpdateHardwareError(
|
|
293
|
-
method.connectId,
|
|
294
|
-
method.deviceId,
|
|
295
|
-
'both',
|
|
296
|
-
currentVersions
|
|
297
|
-
);
|
|
298
|
-
} else if (newVersionStatus === 'required') {
|
|
299
|
-
throw createNewFirmwareForceUpdateHardwareError(
|
|
300
|
-
method.connectId,
|
|
301
|
-
method.deviceId,
|
|
302
|
-
'firmware',
|
|
303
|
-
currentVersions
|
|
304
|
-
);
|
|
305
|
-
} else {
|
|
306
|
-
throw createNewFirmwareForceUpdateHardwareError(
|
|
307
|
-
method.connectId,
|
|
308
|
-
method.deviceId,
|
|
309
|
-
'ble',
|
|
310
|
-
currentVersions
|
|
311
|
-
);
|
|
312
|
-
}
|
|
270
|
+
throw createNewFirmwareForceUpdateHardwareError(method.connectId, method.deviceId);
|
|
313
271
|
}
|
|
314
272
|
|
|
315
273
|
if (versionRange) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
semver.lt(currentFirmwareVersion, versionRange.min)
|
|
319
|
-
) {
|
|
274
|
+
const currentVersion = getDeviceFirmwareVersion(device.features).join('.');
|
|
275
|
+
if (semver.valid(versionRange.min) && semver.lt(currentVersion, versionRange.min)) {
|
|
320
276
|
if (newVersionStatus === 'none' || newVersionStatus === 'valid') {
|
|
321
|
-
throw createNewFirmwareUnReleaseHardwareError(
|
|
322
|
-
currentFirmwareVersion,
|
|
323
|
-
versionRange.min,
|
|
324
|
-
method.name
|
|
325
|
-
);
|
|
277
|
+
throw createNewFirmwareUnReleaseHardwareError(currentVersion, versionRange.min);
|
|
326
278
|
}
|
|
327
279
|
|
|
328
280
|
return Promise.reject(
|
|
329
|
-
createNeedUpgradeFirmwareHardwareError(
|
|
330
|
-
currentFirmwareVersion,
|
|
331
|
-
versionRange.min,
|
|
332
|
-
method.name
|
|
333
|
-
)
|
|
281
|
+
createNeedUpgradeFirmwareHardwareError(currentVersion, versionRange.min)
|
|
334
282
|
);
|
|
335
283
|
}
|
|
336
284
|
if (
|
|
337
285
|
versionRange.max &&
|
|
338
286
|
semver.valid(versionRange.max) &&
|
|
339
|
-
semver.gte(
|
|
287
|
+
semver.gte(currentVersion, versionRange.max)
|
|
340
288
|
) {
|
|
341
|
-
return Promise.reject(
|
|
342
|
-
createDeprecatedHardwareError(currentFirmwareVersion, versionRange.max, method.name)
|
|
343
|
-
);
|
|
289
|
+
return Promise.reject(createDeprecatedHardwareError(currentVersion, versionRange.max));
|
|
344
290
|
}
|
|
345
291
|
} else if (method.strictCheckDeviceSupport) {
|
|
346
|
-
throw ERRORS.TypedError(
|
|
347
|
-
HardwareErrorCode.DeviceNotSupportMethod,
|
|
348
|
-
`Method '${method.name}' is not supported by this device`
|
|
349
|
-
);
|
|
292
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotSupportMethod);
|
|
350
293
|
}
|
|
351
294
|
}
|
|
352
295
|
|
|
@@ -735,6 +678,7 @@ const ensureConnected = async (
|
|
|
735
678
|
HardwareErrorCode.BleWriteCharacteristicError,
|
|
736
679
|
HardwareErrorCode.BleAlreadyConnected,
|
|
737
680
|
HardwareErrorCode.FirmwareUpdateLimitOneDevice,
|
|
681
|
+
HardwareErrorCode.SelectDevice,
|
|
738
682
|
HardwareErrorCode.DeviceDetectInBootloaderMode,
|
|
739
683
|
HardwareErrorCode.BleCharacteristicNotifyChangeFailure,
|
|
740
684
|
HardwareErrorCode.WebDeviceNotFoundOrNeedsPermission,
|
|
@@ -777,6 +721,10 @@ export const cancel = (context: CoreContext, connectId?: string) => {
|
|
|
777
721
|
// }
|
|
778
722
|
// setPrePendingCallPromise(device?.interruptionFromUser());
|
|
779
723
|
// requestQueue.abortRequestsByConnectId(connectId);
|
|
724
|
+
|
|
725
|
+
// cancel callback tasks
|
|
726
|
+
requestQueue.cancelCallbackTasks(connectId);
|
|
727
|
+
|
|
780
728
|
const requestIds = requestQueue.getRequestTasksId();
|
|
781
729
|
Log.debug(
|
|
782
730
|
`Cancel Api connect requestQueues: length:${requestIds.length} requestIds:${requestIds.join(
|
|
@@ -910,7 +858,7 @@ const onDevicePinHandler = async (...[device, type, callback]: DeviceEvents['pin
|
|
|
910
858
|
callback(null, uiResp.payload);
|
|
911
859
|
};
|
|
912
860
|
|
|
913
|
-
const onDeviceButtonHandler = (...[device, request]: [...DeviceEvents['button']]) => {
|
|
861
|
+
export const onDeviceButtonHandler = (...[device, request]: [...DeviceEvents['button']]) => {
|
|
914
862
|
postMessage(createDeviceMessage(DEVICE.BUTTON, { ...request, device: device.toMessageObject() }));
|
|
915
863
|
|
|
916
864
|
if (request.code === 'ButtonRequest_PinEntry' || request.code === 'ButtonRequest_AttachPin') {
|
|
@@ -1014,7 +962,7 @@ const removeUiPromise = (promise: Deferred<any>) => {
|
|
|
1014
962
|
export default class Core extends EventEmitter {
|
|
1015
963
|
private requestQueue = new RequestQueue();
|
|
1016
964
|
|
|
1017
|
-
//
|
|
965
|
+
// background task
|
|
1018
966
|
private prePendingCallPromise: Promise<void> | undefined;
|
|
1019
967
|
|
|
1020
968
|
private methodSynchronize = getSynchronize();
|
|
@@ -1027,6 +975,13 @@ export default class Core extends EventEmitter {
|
|
|
1027
975
|
setPrePendingCallPromise: (promise: Promise<void> | undefined) => {
|
|
1028
976
|
this.prePendingCallPromise = promise;
|
|
1029
977
|
},
|
|
978
|
+
// callback 任务管理
|
|
979
|
+
registerCallbackTask: (connectId: string, callbackPromise: Deferred<any>) => {
|
|
980
|
+
this.requestQueue.registerPendingCallbackTask(connectId, callbackPromise);
|
|
981
|
+
},
|
|
982
|
+
waitForCallbackTasks: (connectId: string) =>
|
|
983
|
+
this.requestQueue.waitForPendingCallbackTasks(connectId),
|
|
984
|
+
cancelCallbackTasks: (connectId: string) => this.requestQueue.cancelCallbackTasks(connectId),
|
|
1030
985
|
};
|
|
1031
986
|
}
|
|
1032
987
|
|
|
@@ -1077,6 +1032,11 @@ export default class Core extends EventEmitter {
|
|
|
1077
1032
|
cancel(this.getCoreContext(), message.payload.connectId);
|
|
1078
1033
|
break;
|
|
1079
1034
|
}
|
|
1035
|
+
case IFRAME.CALLBACK: {
|
|
1036
|
+
Log.log('callback message: ', message);
|
|
1037
|
+
postMessage(message);
|
|
1038
|
+
break;
|
|
1039
|
+
}
|
|
1080
1040
|
default:
|
|
1081
1041
|
break;
|
|
1082
1042
|
}
|
package/src/device/Device.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
|
+
import semver from 'semver';
|
|
2
3
|
import { OneKeyDeviceInfo as DeviceDescriptor } from '@onekeyfe/hd-transport';
|
|
3
4
|
import {
|
|
4
5
|
createDeferred,
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
getDeviceType,
|
|
17
18
|
getDeviceUUID,
|
|
18
19
|
getLogger,
|
|
20
|
+
getMethodVersionRange,
|
|
19
21
|
LoggerNames,
|
|
20
22
|
} from '../utils';
|
|
21
23
|
import {
|
|
@@ -28,6 +30,7 @@ import type DeviceConnector from './DeviceConnector';
|
|
|
28
30
|
import { DeviceCommands, PassphrasePromptResponse } from './DeviceCommands';
|
|
29
31
|
|
|
30
32
|
import {
|
|
33
|
+
type DeviceFirmwareRange,
|
|
31
34
|
EOneKeyDeviceMode,
|
|
32
35
|
type Device as DeviceTyped,
|
|
33
36
|
type Features,
|
|
@@ -43,6 +46,7 @@ import {
|
|
|
43
46
|
import { PROTO } from '../constants';
|
|
44
47
|
import { DataManager } from '../data-manager';
|
|
45
48
|
import TransportManager from '../data-manager/TransportManager';
|
|
49
|
+
import { toHardened } from '../api/helpers/pathUtils';
|
|
46
50
|
|
|
47
51
|
export type InitOptions = {
|
|
48
52
|
initSession?: boolean;
|
|
@@ -151,6 +155,8 @@ export class Device extends EventEmitter {
|
|
|
151
155
|
|
|
152
156
|
passphraseState: string | undefined = undefined;
|
|
153
157
|
|
|
158
|
+
pendingCallbackPromise?: Deferred<void>;
|
|
159
|
+
|
|
154
160
|
constructor(descriptor: DeviceDescriptor) {
|
|
155
161
|
super();
|
|
156
162
|
this.originalDescriptor = descriptor;
|
|
@@ -264,6 +270,18 @@ export class Device extends EventEmitter {
|
|
|
264
270
|
(this.isUsedHere() && !this.keepSession && this.mainId) ||
|
|
265
271
|
(this.mainId && DataManager.isBleConnect(env))
|
|
266
272
|
) {
|
|
273
|
+
// wait for callback tasks to complete before releasing device
|
|
274
|
+
if (this.pendingCallbackPromise) {
|
|
275
|
+
try {
|
|
276
|
+
Log.debug(
|
|
277
|
+
'Waiting for callback tasks to complete before releasing device (in release method)'
|
|
278
|
+
);
|
|
279
|
+
await this.pendingCallbackPromise.promise;
|
|
280
|
+
} catch (error) {
|
|
281
|
+
Log.error('Error waiting for callback tasks in release method:', error);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
267
285
|
if (this.commands) {
|
|
268
286
|
this.commands.dispose(false);
|
|
269
287
|
if (this.commands.callPromise) {
|
|
@@ -732,6 +750,54 @@ export class Device extends EventEmitter {
|
|
|
732
750
|
return res.message;
|
|
733
751
|
}
|
|
734
752
|
|
|
753
|
+
supportUnlockVersionRange(): DeviceFirmwareRange {
|
|
754
|
+
return {
|
|
755
|
+
pro: {
|
|
756
|
+
min: '4.15.0',
|
|
757
|
+
},
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
async unlockDevice() {
|
|
762
|
+
const firmwareVersion = getDeviceFirmwareVersion(this.features)?.join('.');
|
|
763
|
+
const versionRange = getMethodVersionRange(
|
|
764
|
+
this.features,
|
|
765
|
+
type => this.supportUnlockVersionRange()[type]
|
|
766
|
+
);
|
|
767
|
+
|
|
768
|
+
if (versionRange && semver.gte(firmwareVersion, versionRange.min)) {
|
|
769
|
+
const res = await this.commands.typedCall('UnLockDevice', 'UnLockDeviceResponse');
|
|
770
|
+
if (this.features) {
|
|
771
|
+
this.features.unlocked = res.message.unlocked == null ? null : res.message.unlocked;
|
|
772
|
+
this.features.unlocked_attach_pin =
|
|
773
|
+
res.message.unlocked_attach_pin == null ? undefined : res.message.unlocked_attach_pin;
|
|
774
|
+
this.features.passphrase_protection =
|
|
775
|
+
res.message.passphrase_protection == null ? null : res.message.passphrase_protection;
|
|
776
|
+
|
|
777
|
+
return Promise.resolve(this.features);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const featuresRes = await this.commands.typedCall('GetFeatures', 'Features');
|
|
781
|
+
this._updateFeatures(featuresRes.message);
|
|
782
|
+
return Promise.resolve(featuresRes.message);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
const { type } = await this.commands.typedCall('GetAddress', 'Address', {
|
|
786
|
+
address_n: [toHardened(44), toHardened(1), toHardened(0), 0, 0],
|
|
787
|
+
coin_name: 'Testnet',
|
|
788
|
+
script_type: 'SPENDADDRESS',
|
|
789
|
+
show_display: false,
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
// @ts-expect-error
|
|
793
|
+
if (type === 'CallMethodError') {
|
|
794
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'unlock device error');
|
|
795
|
+
}
|
|
796
|
+
const res = await this.commands.typedCall('GetFeatures', 'Features');
|
|
797
|
+
this._updateFeatures(res.message);
|
|
798
|
+
return Promise.resolve(res.message);
|
|
799
|
+
}
|
|
800
|
+
|
|
735
801
|
async checkPassphraseStateSafety(
|
|
736
802
|
passphraseState?: string,
|
|
737
803
|
useEmptyPassphrase?: boolean,
|
package/src/events/call.ts
CHANGED
|
@@ -58,6 +58,16 @@ export interface IFrameSwitchTransportMessage {
|
|
|
58
58
|
payload: { env: ConnectSettings['env'] };
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
export interface IFrameCallbackMessage {
|
|
62
|
+
event: typeof IFRAME.CALLBACK;
|
|
63
|
+
type: typeof IFRAME.CALLBACK;
|
|
64
|
+
payload: {
|
|
65
|
+
callbackId: string;
|
|
66
|
+
data?: any;
|
|
67
|
+
error?: any;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
61
71
|
export const RESPONSE_EVENT = 'RESPONSE_EVENT';
|
|
62
72
|
|
|
63
73
|
export interface MethodResponseMessage {
|
package/src/events/core.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { HardwareError } from '@onekeyfe/hd-shared';
|
|
2
2
|
import { Unsuccessful } from '../types/params';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
IFrameCallMessage,
|
|
5
|
+
IFrameCancelMessage,
|
|
6
|
+
IFrameSwitchTransportMessage,
|
|
7
|
+
IFrameCallbackMessage,
|
|
8
|
+
} from './call';
|
|
4
9
|
import { DeviceEventMessage } from './device';
|
|
5
10
|
import { IFrameEventMessage } from './iframe';
|
|
6
11
|
import { UiEventMessage } from './ui-request';
|
|
@@ -18,6 +23,7 @@ export type CoreMessage = {
|
|
|
18
23
|
| IFrameCallMessage
|
|
19
24
|
| IFrameCancelMessage
|
|
20
25
|
| IFrameSwitchTransportMessage
|
|
26
|
+
| IFrameCallbackMessage
|
|
21
27
|
| UiResponseMessage
|
|
22
28
|
| UiEventMessage
|
|
23
29
|
| DeviceEventMessage
|
package/src/events/iframe.ts
CHANGED
|
@@ -8,6 +8,7 @@ export const IFRAME = {
|
|
|
8
8
|
CALL: 'iframe-call',
|
|
9
9
|
CANCEL: 'iframe-cancel',
|
|
10
10
|
SWITCH_TRANSPORT: 'iframe-switch-transport',
|
|
11
|
+
CALLBACK: 'iframe-callback',
|
|
11
12
|
} as const;
|
|
12
13
|
|
|
13
14
|
export interface IFrameInit {
|
|
@@ -29,7 +30,17 @@ export interface IFrameSwitchTransport {
|
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
export
|
|
33
|
+
export interface IFrameCallback {
|
|
34
|
+
type: typeof IFRAME.CALLBACK;
|
|
35
|
+
payload: {
|
|
36
|
+
callbackId: string;
|
|
37
|
+
data?: any;
|
|
38
|
+
error?: any;
|
|
39
|
+
finished?: boolean;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type IFrameEvent = IFrameInit | IFrameBridge | IFrameSwitchTransport | IFrameCallback;
|
|
33
44
|
export type IFrameEventMessage = IFrameEvent & { event: typeof UI_EVENT };
|
|
34
45
|
|
|
35
46
|
export const createIFrameMessage: MessageFactoryFn<typeof UI_EVENT, IFrameEvent> = (
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { inject, InjectApi } from './inject';
|
|
1
|
+
import { inject, InjectApi, executeCallback, cleanupCallback } from './inject';
|
|
2
2
|
import { lowLevelInject, LowLevelInjectApi, LowLevelCoreApi } from './lowLevelInject';
|
|
3
3
|
import { topLevelInject } from './topLevelInject';
|
|
4
4
|
import { CoreApi } from './types/api';
|
|
@@ -14,6 +14,7 @@ export * from './data-manager';
|
|
|
14
14
|
export * from './events';
|
|
15
15
|
export * from './types';
|
|
16
16
|
export { whitelist, whitelistExtension } from './data/config';
|
|
17
|
+
export { executeCallback, cleanupCallback };
|
|
17
18
|
|
|
18
19
|
const HardwareSdk = ({
|
|
19
20
|
init,
|
package/src/inject.ts
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
2
|
import { CallMethod } from './events';
|
|
3
3
|
import { CoreApi } from './types/api';
|
|
4
|
+
import type { AllNetworkAddress } from './types/api/allNetworkGetAddress';
|
|
5
|
+
|
|
6
|
+
type CallbackFunction = (data?: any, error?: { message: string; code?: number }) => void;
|
|
7
|
+
|
|
8
|
+
const callbackManager = new Map<string, CallbackFunction>();
|
|
9
|
+
|
|
10
|
+
const generateCallbackId = () =>
|
|
11
|
+
`callback_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
12
|
+
|
|
13
|
+
const registerCallback = (id: string, callback: CallbackFunction) => {
|
|
14
|
+
callbackManager.set(id, callback);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const executeCallback = (id: string, ...args: any[]) => {
|
|
18
|
+
const callback = callbackManager.get(id);
|
|
19
|
+
if (callback) {
|
|
20
|
+
callback(...args);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const cleanupCallback = (id: string) => {
|
|
25
|
+
callbackManager.delete(id);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export { executeCallback, cleanupCallback };
|
|
4
29
|
|
|
5
30
|
export interface InjectApi {
|
|
6
31
|
call: CallMethod;
|
|
@@ -147,6 +172,28 @@ export const createCoreApi = (
|
|
|
147
172
|
|
|
148
173
|
allNetworkGetAddress: (connectId, deviceId, params) =>
|
|
149
174
|
call({ ...params, connectId, deviceId, method: 'allNetworkGetAddress' }),
|
|
175
|
+
allNetworkGetAddressByLoop: (connectId, deviceId, params) => {
|
|
176
|
+
const { onLoopItemResponse, onAllItemsResponse, ...restParams } = params;
|
|
177
|
+
|
|
178
|
+
const callbackId = generateCallbackId();
|
|
179
|
+
registerCallback(callbackId, onLoopItemResponse);
|
|
180
|
+
|
|
181
|
+
const callbackIdFinish = generateCallbackId();
|
|
182
|
+
registerCallback(callbackIdFinish, (data?: AllNetworkAddress[]) => {
|
|
183
|
+
onAllItemsResponse?.(data);
|
|
184
|
+
cleanupCallback(callbackIdFinish);
|
|
185
|
+
cleanupCallback(callbackId);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return call({
|
|
189
|
+
...restParams,
|
|
190
|
+
connectId,
|
|
191
|
+
deviceId,
|
|
192
|
+
method: 'allNetworkGetAddressByLoop',
|
|
193
|
+
callbackId,
|
|
194
|
+
callbackIdFinish,
|
|
195
|
+
});
|
|
196
|
+
},
|
|
150
197
|
|
|
151
198
|
evmGetAddress: (connectId, deviceId, params) =>
|
|
152
199
|
call({ ...params, connectId, deviceId, method: 'evmGetAddress' }),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CommonParams, Response } from '../params';
|
|
1
|
+
import type { CommonParams, Response, Unsuccessful } from '../params';
|
|
2
2
|
import type { CardanoAddressParameters } from './cardano';
|
|
3
3
|
|
|
4
4
|
export type INetwork =
|
|
@@ -91,10 +91,10 @@ type AllNetworkAddressPayload =
|
|
|
91
91
|
xpubSegwit: string;
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
-
export type AllNetworkAddress =
|
|
94
|
+
export type AllNetworkAddress = AllNetworkAddressParams & {
|
|
95
95
|
success: boolean;
|
|
96
96
|
payload?:
|
|
97
|
-
| AllNetworkAddressPayload
|
|
97
|
+
| (AllNetworkAddressPayload & { rootFingerprint: number })
|
|
98
98
|
| {
|
|
99
99
|
error: string;
|
|
100
100
|
code: number;
|
|
@@ -108,8 +108,22 @@ export type AllNetworkGetAddressParams = {
|
|
|
108
108
|
bundle: AllNetworkAddressParams[];
|
|
109
109
|
};
|
|
110
110
|
|
|
111
|
+
export type AllNetworkGetAddressParamsByLoop = AllNetworkGetAddressParams & {
|
|
112
|
+
callbackId?: string;
|
|
113
|
+
callbackIdFinish?: string;
|
|
114
|
+
onLoopItemResponse: (data?: AllNetworkAddress) => void;
|
|
115
|
+
onAllItemsResponse: (data?: AllNetworkAddress[], error?: Unsuccessful) => void;
|
|
116
|
+
};
|
|
117
|
+
|
|
111
118
|
export declare function allNetworkGetAddress(
|
|
112
119
|
connectId: string,
|
|
113
120
|
deviceId: string,
|
|
114
121
|
params: CommonParams & AllNetworkGetAddressParams
|
|
115
122
|
): Response<AllNetworkAddress[]>;
|
|
123
|
+
|
|
124
|
+
export declare function allNetworkGetAddressByLoop(
|
|
125
|
+
connectId: string,
|
|
126
|
+
deviceId: string,
|
|
127
|
+
params: CommonParams & AllNetworkGetAddressParamsByLoop
|
|
128
|
+
// only return empty array
|
|
129
|
+
): Response<AllNetworkAddress[]>;
|