@onekeyfe/hd-core 1.2.2-alpha.12 → 1.2.2-alpha.122
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/README.md +1 -1
- package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +503 -10
- package/__tests__/DeviceCommands.test.ts +254 -1
- package/__tests__/core-error-output.test.ts +169 -1
- package/__tests__/device-lifecycle-events.test.ts +379 -15
- package/__tests__/open-wallet-session-error-response.test.ts +2 -2
- package/__tests__/open-wallet-session.test.ts +8 -411
- package/__tests__/protocol-v2.test.ts +86 -0
- package/__tests__/public-device-state-api.test.ts +2 -7
- package/__tests__/search-devices.test.ts +196 -8
- package/__tests__/sol-sign-offchain-message.test.ts +64 -0
- package/dist/api/GetFeatures.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/SearchDevices.d.ts +2 -15
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts +1 -0
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddress.d.ts +2 -0
- package/dist/api/allnetwork/AllNetworkGetAddress.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts +7 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressByLoop.d.ts.map +1 -1
- package/dist/api/device/DeviceVerify.d.ts.map +1 -1
- package/dist/api/solana/SolSignOffchainMessage.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts +1 -0
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/uiPromiseRegistry.d.ts +1 -1
- package/dist/data-manager/TransportManager.d.ts +2 -0
- package/dist/data-manager/TransportManager.d.ts.map +1 -1
- package/dist/device/Device.d.ts +2 -0
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +5 -4
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/index.d.ts +35 -30
- package/dist/index.js +558 -234
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/getFeatures.d.ts.map +1 -1
- package/dist/types/api/getPassphraseState.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +1 -10
- package/dist/types/api/openWalletSession.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +3 -4
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/api/solSignOffchainMessage.d.ts +1 -0
- package/dist/types/api/solSignOffchainMessage.d.ts.map +1 -1
- package/dist/types/params.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/GetFeatures.ts +1 -0
- package/src/api/GetPassphraseState.ts +1 -0
- package/src/api/OpenWalletSession.ts +7 -77
- package/src/api/SearchDevices.ts +121 -27
- package/src/api/UploadPortfolio.ts +5 -4
- package/src/api/allnetwork/AllNetworkGetAddress.ts +79 -45
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +95 -24
- package/src/api/allnetwork/AllNetworkGetAddressByLoop.ts +3 -0
- package/src/api/device/DeviceVerify.ts +8 -0
- package/src/api/solana/SolSignOffchainMessage.ts +44 -4
- package/src/core/RequestQueue.ts +20 -0
- package/src/core/index.ts +156 -50
- package/src/data/messages/messages-protocol-v2.json +49 -33
- package/src/data/messages/messages.json +8 -5
- package/src/data-manager/TransportManager.ts +14 -3
- package/src/device/Device.ts +58 -27
- package/src/device/DeviceCommands.ts +29 -2
- package/src/protocols/protocol-v2/walletSession.ts +7 -0
- package/src/types/api/getFeatures.ts +2 -1
- package/src/types/api/getPassphraseState.ts +2 -5
- package/src/types/api/openWalletSession.ts +7 -19
- package/src/types/api/protocolV2.ts +3 -4
- package/src/types/api/solSignOffchainMessage.ts +2 -0
- package/src/types/params.ts +7 -0
package/src/device/Device.ts
CHANGED
|
@@ -297,6 +297,8 @@ export class Device extends EventEmitter {
|
|
|
297
297
|
/** Resolves only after the active run has completed its release path. */
|
|
298
298
|
private runCleanupPromise?: Promise<void>;
|
|
299
299
|
|
|
300
|
+
private userInterruption?: { attempt: number; promise: Promise<void> };
|
|
301
|
+
|
|
300
302
|
externalState: string[] = [];
|
|
301
303
|
|
|
302
304
|
unavailableCapabilities: UnavailableCapabilities = {};
|
|
@@ -1462,6 +1464,10 @@ export class Device extends EventEmitter {
|
|
|
1462
1464
|
this.invalidateProtocolV2RuntimeState();
|
|
1463
1465
|
}
|
|
1464
1466
|
|
|
1467
|
+
waitForRunCleanup(): Promise<void> {
|
|
1468
|
+
return this.runCleanupPromise ?? Promise.resolve();
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1465
1471
|
async run(fn?: () => Promise<void>, options?: RunOptions) {
|
|
1466
1472
|
if (this.runPromise) {
|
|
1467
1473
|
await this.interruptionFromOutside();
|
|
@@ -1596,45 +1602,70 @@ export class Device extends EventEmitter {
|
|
|
1596
1602
|
}
|
|
1597
1603
|
}
|
|
1598
1604
|
|
|
1599
|
-
|
|
1605
|
+
interruptionFromUser(): Promise<void> {
|
|
1606
|
+
const attempt = this.connectionAttempt;
|
|
1607
|
+
if (this.userInterruption?.attempt === attempt) {
|
|
1608
|
+
return this.userInterruption.promise;
|
|
1609
|
+
}
|
|
1600
1610
|
const error = ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromUser);
|
|
1601
|
-
this.interruptedAttempt =
|
|
1611
|
+
this.interruptedAttempt = attempt;
|
|
1602
1612
|
const cleanupPromise = this.runCleanupPromise;
|
|
1603
|
-
const { cancelableAction } = this;
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1613
|
+
const { cancelableAction, commands, runPromise, deviceConnector, mainId } = this;
|
|
1614
|
+
const sendFallbackCancel = this.shouldSendFallbackProtocolCancel();
|
|
1615
|
+
const acquired = this.hasDeviceAcquire();
|
|
1616
|
+
const promise = (async () => {
|
|
1617
|
+
try {
|
|
1618
|
+
if (cancelableAction) {
|
|
1619
|
+
await cancelableAction(error);
|
|
1620
|
+
} else if (sendFallbackCancel) {
|
|
1621
|
+
await commands?.cancelDevice?.().catch(cancelError => {
|
|
1622
|
+
Log.debug('Protocol V2 fallback cancel error', cancelError);
|
|
1623
|
+
});
|
|
1624
|
+
} else if (!acquired) {
|
|
1625
|
+
// Abort setup without acquiring a session just to send Cancel.
|
|
1626
|
+
if (mainId && deviceConnector?.disconnect) {
|
|
1627
|
+
// The connector drops the link silently, so without this line a
|
|
1628
|
+
// user-cancel teardown is indistinguishable in field logs from an
|
|
1629
|
+
// idle keep-alive release or a device that left on its own.
|
|
1630
|
+
Log.debug(
|
|
1631
|
+
'interruptionFromUser: disconnecting device without acquire, mainId:',
|
|
1632
|
+
mainId
|
|
1633
|
+
);
|
|
1634
|
+
await deviceConnector.disconnect(mainId);
|
|
1635
|
+
}
|
|
1636
|
+
if (this.connectionAttempt === attempt) this.markTransportDisconnected();
|
|
1637
|
+
}
|
|
1638
|
+
await commands?.cancel();
|
|
1639
|
+
} catch (cleanupError) {
|
|
1640
|
+
Log.warn('User cancellation cleanup failed; disconnecting device', cleanupError);
|
|
1641
|
+
if (mainId && deviceConnector?.disconnect) {
|
|
1642
|
+
await deviceConnector.disconnect(mainId).catch(disconnectError => {
|
|
1643
|
+
Log.debug('User cancellation fallback disconnect failed', disconnectError);
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
if (this.connectionAttempt === attempt) this.markTransportDisconnected();
|
|
1647
|
+
} finally {
|
|
1648
|
+
runPromise?.reject(error);
|
|
1649
|
+
if (this.runPromise === runPromise) this.runPromise = null;
|
|
1650
|
+
await cleanupPromise?.catch(() => undefined);
|
|
1617
1651
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
this.runPromise.reject(error);
|
|
1624
|
-
this.runPromise = null;
|
|
1625
|
-
}
|
|
1626
|
-
await cleanupPromise?.catch(() => undefined);
|
|
1652
|
+
})();
|
|
1653
|
+
// Keep the settled promise for this attempt so a duplicate close/finally
|
|
1654
|
+
// cannot send a second wire Cancel. A new attempt gets its own cleanup.
|
|
1655
|
+
this.userInterruption = { attempt, promise };
|
|
1656
|
+
return promise;
|
|
1627
1657
|
}
|
|
1628
1658
|
|
|
1629
1659
|
setCancelableAction(callback: (err?: Error) => Promise<unknown>) {
|
|
1630
|
-
|
|
1660
|
+
const action = (e?: Error) =>
|
|
1631
1661
|
callback(e)
|
|
1632
1662
|
.catch(e2 => {
|
|
1633
1663
|
Log.debug('cancelableAction error', e2);
|
|
1634
1664
|
})
|
|
1635
1665
|
.finally(() => {
|
|
1636
|
-
this.clearCancelableAction();
|
|
1666
|
+
if (this.cancelableAction === action) this.clearCancelableAction();
|
|
1637
1667
|
});
|
|
1668
|
+
this.cancelableAction = action;
|
|
1638
1669
|
}
|
|
1639
1670
|
|
|
1640
1671
|
clearCancelableAction() {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from '@onekeyfe/hd-transport';
|
|
12
12
|
|
|
13
13
|
import TransportManager from '../data-manager/TransportManager';
|
|
14
|
-
import { LoggerNames, getLogger, patchFeatures } from '../utils';
|
|
14
|
+
import { LoggerNames, getLogger, patchFeatures, wait } from '../utils';
|
|
15
15
|
import { DEVICE, type PassphraseRequestPayload } from '../events';
|
|
16
16
|
import { DeviceModelToTypes } from '../types';
|
|
17
17
|
import {
|
|
@@ -198,6 +198,8 @@ export class DeviceCommands {
|
|
|
198
198
|
|
|
199
199
|
disposed: boolean;
|
|
200
200
|
|
|
201
|
+
private disposalToken?: object;
|
|
202
|
+
|
|
201
203
|
callPromise?: Promise<DefaultMessageResponse>;
|
|
202
204
|
|
|
203
205
|
constructor(device: Device, mainId: string) {
|
|
@@ -212,6 +214,7 @@ export class DeviceCommands {
|
|
|
212
214
|
|
|
213
215
|
async dispose(_cancelRequest: boolean) {
|
|
214
216
|
this.disposed = true;
|
|
217
|
+
this.disposalToken = {};
|
|
215
218
|
await this.transport.cancel?.();
|
|
216
219
|
}
|
|
217
220
|
|
|
@@ -439,7 +442,31 @@ export class DeviceCommands {
|
|
|
439
442
|
msg?: DefaultMessageResponse['message'],
|
|
440
443
|
options?: TransportCallOptions
|
|
441
444
|
) {
|
|
442
|
-
const
|
|
445
|
+
const { disposalToken } = this;
|
|
446
|
+
let resp = await this.call(type, msg, options);
|
|
447
|
+
// Session busy failures precede wallet derivation or reject an occupied flow.
|
|
448
|
+
// Three retries cover the 240 ms PIN dismissal animation on older V2 firmware.
|
|
449
|
+
for (
|
|
450
|
+
let retry = 0;
|
|
451
|
+
retry < 3 &&
|
|
452
|
+
this.device.isProtocolV2() &&
|
|
453
|
+
DEVICE_SESSION_CALLS.has(type) &&
|
|
454
|
+
resp.type === 'Failure' &&
|
|
455
|
+
(resp.message.code as string | FailureType) === 'Failure_ProcessError' &&
|
|
456
|
+
resp.message.subcode === DeviceSessionErrorCode.DeviceSessionError_Busy;
|
|
457
|
+
retry += 1
|
|
458
|
+
) {
|
|
459
|
+
await wait(100);
|
|
460
|
+
if (this.device.wasInterruptedByUser()) {
|
|
461
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromUser);
|
|
462
|
+
}
|
|
463
|
+
this.checkDisposed();
|
|
464
|
+
if (this.disposalToken !== disposalToken) {
|
|
465
|
+
// React Native can revive this instance. A stale retry must not release the new run.
|
|
466
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'DeviceCommands lifecycle changed');
|
|
467
|
+
}
|
|
468
|
+
resp = await this.call(type, msg, options);
|
|
469
|
+
}
|
|
443
470
|
return this._filterCommonTypes(resp, type, options);
|
|
444
471
|
}
|
|
445
472
|
|
|
@@ -268,6 +268,7 @@ export async function getProtocolV2WalletSession(
|
|
|
268
268
|
? device.getInternalState()
|
|
269
269
|
: undefined;
|
|
270
270
|
let response;
|
|
271
|
+
let walletSelectionRequested = false;
|
|
271
272
|
let resumed = false;
|
|
272
273
|
let walletStatusRefreshed = false;
|
|
273
274
|
const markWalletStatusRefreshed = () => {
|
|
@@ -482,6 +483,7 @@ export async function getProtocolV2WalletSession(
|
|
|
482
483
|
throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
|
|
483
484
|
}
|
|
484
485
|
await lockAttachPinBeforePassphraseSelection();
|
|
486
|
+
walletSelectionRequested = true;
|
|
485
487
|
response = await selectDeviceSession(
|
|
486
488
|
device,
|
|
487
489
|
expectedPassphraseState,
|
|
@@ -508,6 +510,11 @@ export async function getProtocolV2WalletSession(
|
|
|
508
510
|
device.clearInternalState();
|
|
509
511
|
throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
|
|
510
512
|
}
|
|
513
|
+
// Report a fresh selection mismatch before asking for input again.
|
|
514
|
+
if (walletSelectionRequested) {
|
|
515
|
+
clearCurrentWalletSession();
|
|
516
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
|
|
517
|
+
}
|
|
511
518
|
if (options?.onlyMainPin) {
|
|
512
519
|
device.clearStandardInternalState?.();
|
|
513
520
|
await selectStandardWallet(true);
|
|
@@ -2,6 +2,7 @@ import type { CommonParams, Response } from '../params';
|
|
|
2
2
|
import type { Features } from '../device';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* @deprecated Use `getDeviceState` for both Protocol V1 and Protocol V2
|
|
5
|
+
* @deprecated Use `getDeviceState` for both Protocol V1 and Protocol V2.
|
|
6
|
+
* Existing calls still work. Read `payload.identity.deviceId` from `getDeviceState`.
|
|
6
7
|
*/
|
|
7
8
|
export declare function getFeatures(connectId?: string, params?: CommonParams): Response<Features>;
|
|
@@ -3,11 +3,8 @@ import type { CommonParams, Response } from '../params';
|
|
|
3
3
|
export type GetPassphraseStateParams = CommonParams;
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Protocol V1 keeps its parameterless firmware flow. Protocol V2 maps the existing
|
|
9
|
-
* `useEmptyPassphrase` and `initSession` intent to the device-only wallet-session flow.
|
|
10
|
-
* New integrations should prefer `openWalletSession` for explicit wallet intent.
|
|
6
|
+
* @deprecated Use `openWalletSession` for both Protocol V1 and Protocol V2.
|
|
7
|
+
* Existing calls still work. Persist `deviceId + passphraseState`; do not persist firmware `session_id`.
|
|
11
8
|
*/
|
|
12
9
|
export declare function getPassphraseState(
|
|
13
10
|
connectId?: string,
|
|
@@ -3,28 +3,16 @@ import type { CommonParams, Response } from '../params';
|
|
|
3
3
|
export const OpenWalletSessionMode = {
|
|
4
4
|
Standard: 'standard',
|
|
5
5
|
SelectHidden: 'select-hidden',
|
|
6
|
-
ResumeHidden: 'resume-hidden',
|
|
7
6
|
} as const;
|
|
8
7
|
|
|
9
8
|
export type OpenWalletSessionModeValue =
|
|
10
9
|
(typeof OpenWalletSessionMode)[keyof typeof OpenWalletSessionMode];
|
|
11
10
|
|
|
12
|
-
export type OpenWalletSessionParams =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
| {
|
|
19
|
-
mode: typeof OpenWalletSessionMode.SelectHidden;
|
|
20
|
-
deviceId?: never;
|
|
21
|
-
passphraseState?: never;
|
|
22
|
-
}
|
|
23
|
-
| {
|
|
24
|
-
mode: typeof OpenWalletSessionMode.ResumeHidden;
|
|
25
|
-
deviceId: string;
|
|
26
|
-
passphraseState: string;
|
|
27
|
-
};
|
|
11
|
+
export type OpenWalletSessionParams = {
|
|
12
|
+
mode: OpenWalletSessionModeValue;
|
|
13
|
+
deviceId?: never;
|
|
14
|
+
passphraseState?: never;
|
|
15
|
+
};
|
|
28
16
|
|
|
29
17
|
type OpenWalletSessionPayloadBase = {
|
|
30
18
|
protocol: 'V1' | 'V2';
|
|
@@ -45,8 +33,8 @@ export type OpenWalletSessionPayload = OpenWalletSessionPayloadBase &
|
|
|
45
33
|
);
|
|
46
34
|
|
|
47
35
|
/**
|
|
48
|
-
* Opens the standard
|
|
49
|
-
*
|
|
36
|
+
* Opens the standard or hidden wallet through a unified Protocol V1/V2 API.
|
|
37
|
+
* Resume a bound hidden wallet by passing passphraseState on later methods.
|
|
50
38
|
*/
|
|
51
39
|
export declare function openWalletSession(
|
|
52
40
|
connectId: string,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CommonParams, Response } from '../params';
|
|
2
2
|
import type { OnboardingStatus, Success } from '@onekeyfe/hd-transport';
|
|
3
3
|
import type { DeviceRebootParams } from '../../api/protocol-v2/helpers';
|
|
4
|
+
import type { UploadPortfolioParams } from '../../api/UploadPortfolio';
|
|
4
5
|
import type {
|
|
5
6
|
DeviceUploadWallpaperParams,
|
|
6
7
|
DeviceUploadWallpaperResponse,
|
|
@@ -12,6 +13,7 @@ import type {
|
|
|
12
13
|
|
|
13
14
|
// Re-export implementation parameter types as the single source of truth.
|
|
14
15
|
export type { DeviceRebootParams, RebootTypeInput } from '../../api/protocol-v2/helpers';
|
|
16
|
+
export type { UploadPortfolioParams } from '../../api/UploadPortfolio';
|
|
15
17
|
export type {
|
|
16
18
|
DeviceUploadWallpaperParams,
|
|
17
19
|
DeviceUploadWallpaperResponse,
|
|
@@ -67,8 +69,5 @@ export declare function deviceUploadNft(
|
|
|
67
69
|
|
|
68
70
|
export declare function uploadPortfolio(
|
|
69
71
|
connectId: string,
|
|
70
|
-
params:
|
|
71
|
-
packageBase64: string;
|
|
72
|
-
timeoutMs?: number | string;
|
|
73
|
-
}
|
|
72
|
+
params: UploadPortfolioParams
|
|
74
73
|
): Response<FileInfo & { portfolioUpdated: true }>;
|
|
@@ -15,6 +15,8 @@ export type SolSignOffchainMessageParams = {
|
|
|
15
15
|
messageVersion?: SolanaOffChainMessageVersion;
|
|
16
16
|
messageFormat?: SolanaOffChainMessageFormat;
|
|
17
17
|
applicationDomainHex?: string;
|
|
18
|
+
/** 32-byte public keys encoded as hex, strictly sorted and unique. */
|
|
19
|
+
requiredSigners?: string[];
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
export declare function solSignOffchainMessage(
|
package/src/types/params.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { HardwareConnectProtocol } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
export interface CommonParams {
|
|
4
|
+
/**
|
|
5
|
+
* Keep the transport session after this call instead of releasing it.
|
|
6
|
+
* This is a Device.run() hold flag, not a wallet identity. It does not replace
|
|
7
|
+
* `useEmptyPassphrase`, `passphraseState`, or `openWalletSession`.
|
|
8
|
+
* USB multi-step flows and Electron BLE firmware windows still need this to
|
|
9
|
+
* avoid dropping the link between calls.
|
|
10
|
+
*/
|
|
4
11
|
keepSession?: boolean;
|
|
5
12
|
/**
|
|
6
13
|
* polling connect max retry count
|