@onekeyfe/hd-core 1.2.0-alpha.36 → 1.2.0-alpha.39
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 +5 -1
- package/__tests__/deviceUploadNft.test.ts +187 -0
- package/__tests__/homescreen.test.ts +41 -1
- package/__tests__/logBlockEvent.test.ts +1 -1
- package/__tests__/open-wallet-session.test.ts +161 -44
- package/__tests__/pro2Nft.test.ts +108 -0
- package/__tests__/protocol-v2-unlock-policy.test.ts +86 -35
- package/__tests__/protocol-v2.test.ts +168 -444
- package/__tests__/public-pro2-api-boundary.test.ts +1 -0
- package/__tests__/uiProgressThrottle.test.ts +74 -0
- package/dist/api/BaseMethod.d.ts +3 -1
- package/dist/api/BaseMethod.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/firmware/FirmwareUpdateBaseMethod.d.ts +0 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts +2 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +30 -0
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +34 -2
- package/dist/index.js +544 -496
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/walletSession.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/types/api/protocolV2.d.ts +3 -0
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/utils/homescreen.d.ts +4 -0
- package/dist/utils/homescreen.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.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/pro2Nft.d.ts +29 -0
- package/dist/utils/pro2Nft.d.ts.map +1 -0
- package/dist/utils/pro2Wallpaper.d.ts +10 -0
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/dist/utils/uiProgressThrottle.d.ts +3 -0
- package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +7 -9
- package/src/api/OpenWalletSession.ts +37 -15
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +29 -30
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +0 -7
- package/src/api/helpers/protocolV2FileWrite.ts +11 -4
- package/src/api/index.ts +1 -0
- package/src/api/protocol-v2/DeviceUploadNft.ts +166 -0
- package/src/core/index.ts +83 -82
- package/src/data/messages/messages-protocol-v2.json +10 -15
- package/src/events/logBlockEvent.ts +1 -0
- package/src/inject.ts +1 -0
- package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +117 -0
- package/src/protocols/protocol-v2/walletSession.ts +18 -51
- package/src/types/api/index.ts +2 -0
- package/src/types/api/protocolV2.ts +13 -0
- package/src/utils/homescreen.ts +32 -6
- package/src/utils/index.ts +6 -1
- package/src/utils/pro2Nft.ts +91 -0
- package/src/utils/pro2Wallpaper.ts +35 -8
- package/src/utils/uiProgressThrottle.ts +63 -0
- package/dist/api/firmware/progressThrottle.d.ts +0 -3
- package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
- package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
- package/src/api/firmware/progressThrottle.ts +0 -44
- package/src/protocols/protocol-v2/lockedError.ts +0 -10
- package/src/protocols/protocol-v2/unlockRetry.ts +0 -131
package/src/core/index.ts
CHANGED
|
@@ -55,12 +55,12 @@ import DeviceConnector from '../device/DeviceConnector';
|
|
|
55
55
|
import RequestQueue from './RequestQueue';
|
|
56
56
|
import { registerHardwareUiEventListeners } from './deviceEventRegistration';
|
|
57
57
|
import { getSynchronize } from '../utils/getSynchronize';
|
|
58
|
-
import {
|
|
59
|
-
import { ensureProtocolV2WalletSessionUnlocked } from '../protocols/protocol-v2/walletSession';
|
|
58
|
+
import { runMethodWithUnlockPolicy } from '../protocols/protocol-v2/unlockPolicyRunner';
|
|
60
59
|
import {
|
|
61
60
|
ProtocolV2UiInteractionCoordinator,
|
|
62
61
|
isProtocolV2UiEnabled,
|
|
63
62
|
} from '../protocols/protocol-v2/uiInteraction';
|
|
63
|
+
import { createUiProgressMessageFilter } from '../utils/uiProgressThrottle';
|
|
64
64
|
|
|
65
65
|
import type { ConnectSettings, Features, KnownDevice } from '../types';
|
|
66
66
|
import type { CoreMessage, IFrameCallMessage, UiPromise, UiPromiseResponse } from '../events';
|
|
@@ -155,7 +155,12 @@ export const callAPI = async (context: CoreContext, message: CoreMessage) => {
|
|
|
155
155
|
try {
|
|
156
156
|
method = findMethod(message as IFrameCallMessage);
|
|
157
157
|
method.connector = _connector;
|
|
158
|
-
|
|
158
|
+
const shouldPostMessage = createUiProgressMessageFilter();
|
|
159
|
+
method.postMessage = event => {
|
|
160
|
+
if (shouldPostMessage(event)) {
|
|
161
|
+
postMessage(event);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
159
164
|
method.setContext?.(context);
|
|
160
165
|
|
|
161
166
|
method.requestContext = createRequestContext(method.responseID, method.name, {
|
|
@@ -551,96 +556,92 @@ const onCallDevice = async (
|
|
|
551
556
|
);
|
|
552
557
|
}
|
|
553
558
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
559
|
+
try {
|
|
560
|
+
let deviceIdCheckedDuringUnlockPreflight = false;
|
|
561
|
+
const response = await runMethodWithUnlockPolicy<object>(method, device, {
|
|
562
|
+
uiCoordinator: protocolV2UiCoordinator,
|
|
563
|
+
afterStatusBeforeUnlock: () => {
|
|
564
|
+
if (method.deviceId && method.checkDeviceId) {
|
|
565
|
+
if (!device.checkDeviceId(method.deviceId)) {
|
|
566
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
567
|
+
}
|
|
568
|
+
deviceIdCheckedDuringUnlockPreflight = true;
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
prepare: async () => {
|
|
572
|
+
if (method.deviceId && method.checkDeviceId && !deviceIdCheckedDuringUnlockPreflight) {
|
|
573
|
+
const isSameDeviceID = await checkLiveDeviceId(device, method.deviceId);
|
|
574
|
+
if (!isSameDeviceID) {
|
|
575
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
565
578
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
579
|
+
/**
|
|
580
|
+
* check firmware release info
|
|
581
|
+
*/
|
|
582
|
+
method.checkFirmwareRelease();
|
|
570
583
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
584
|
+
/**
|
|
585
|
+
* check additional supported feature
|
|
586
|
+
*/
|
|
587
|
+
method.checkDeviceSupportFeature();
|
|
575
588
|
|
|
576
|
-
|
|
577
|
-
|
|
589
|
+
// reconfigure messages
|
|
590
|
+
if (_deviceList && device.features && !device.isProtocolV2()) {
|
|
591
|
+
await TransportManager.reconfigure(device.features);
|
|
592
|
+
}
|
|
578
593
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
594
|
+
// Check to see if it is safe to use Passphrase
|
|
595
|
+
checkPassphraseEnableState(method, device.features);
|
|
596
|
+
|
|
597
|
+
if (shouldCheckPassphraseState(method, device)) {
|
|
598
|
+
// check version
|
|
599
|
+
const support = device.supportNewPassphrase();
|
|
600
|
+
if (!support.support) {
|
|
601
|
+
throw ERRORS.TypedError(
|
|
602
|
+
HardwareErrorCode.DeviceNotSupportPassphrase,
|
|
603
|
+
`Device not support passphrase, please update to ${support.require}`,
|
|
604
|
+
{
|
|
605
|
+
require: support.require,
|
|
606
|
+
}
|
|
607
|
+
);
|
|
589
608
|
}
|
|
590
|
-
)
|
|
591
|
-
);
|
|
592
|
-
}
|
|
593
609
|
|
|
594
|
-
|
|
595
|
-
|
|
610
|
+
// Check Device passphrase State after the Protocol V2 pre-unlock gate.
|
|
611
|
+
const passphraseStateSafety = await device.checkPassphraseStateSafety(
|
|
612
|
+
method.payload?.passphraseState,
|
|
613
|
+
method.payload?.useEmptyPassphrase,
|
|
614
|
+
method.payload?.skipPassphraseCheck,
|
|
615
|
+
hasDeriveCardano(method)
|
|
616
|
+
);
|
|
596
617
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
method.payload?.passphraseState,
|
|
600
|
-
method.payload?.useEmptyPassphrase,
|
|
601
|
-
method.payload?.skipPassphraseCheck,
|
|
602
|
-
hasDeriveCardano(method)
|
|
603
|
-
);
|
|
618
|
+
// Double check, handles the special case of Touch/Pro
|
|
619
|
+
checkPassphraseEnableState(method, device.features);
|
|
604
620
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
DevicePool.clearDeviceCache(method.payload.connectId);
|
|
610
|
-
return Promise.reject(
|
|
611
|
-
ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError)
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
// Protocol V2 emits the PIN phase completion from DeviceSessionAskPin.
|
|
616
|
-
// Keep the legacy compatibility close for Protocol V1 only.
|
|
617
|
-
if (!device.isProtocolV2()) {
|
|
618
|
-
postMessage(createUiMessage(UI_REQUEST.CLOSE_UI_PIN_WINDOW));
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
+
if (!passphraseStateSafety) {
|
|
622
|
+
DevicePool.clearDeviceCache(method.payload.connectId);
|
|
623
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
|
|
624
|
+
}
|
|
621
625
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
? e
|
|
629
|
-
: ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'open safety check failed.');
|
|
630
|
-
// messageResponse = createResponseMessage(method.responseID, false, { error });
|
|
631
|
-
// requestQueue.resolveRequest(method.responseID, messageResponse);
|
|
632
|
-
// return;
|
|
633
|
-
throw error;
|
|
634
|
-
}
|
|
626
|
+
// Protocol V2 emits the PIN phase completion from DeviceSessionAskPin.
|
|
627
|
+
// Keep the legacy compatibility close for Protocol V1 only.
|
|
628
|
+
if (!device.isProtocolV2()) {
|
|
629
|
+
postMessage(createUiMessage(UI_REQUEST.CLOSE_UI_PIN_WINDOW));
|
|
630
|
+
}
|
|
631
|
+
}
|
|
635
632
|
|
|
636
|
-
|
|
633
|
+
// Automatic check safety_check level for Kovan, Ropsten, Rinkeby, Goerli test networks.
|
|
634
|
+
try {
|
|
635
|
+
await method.checkSafetyLevelOnTestNet();
|
|
636
|
+
} catch (e) {
|
|
637
|
+
throw e instanceof HardwareError
|
|
638
|
+
? e
|
|
639
|
+
: ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'open safety check failed.');
|
|
640
|
+
}
|
|
637
641
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
device,
|
|
642
|
-
protocolV2UiCoordinator
|
|
643
|
-
);
|
|
642
|
+
method.device?.commands?.checkDisposed();
|
|
643
|
+
},
|
|
644
|
+
});
|
|
644
645
|
messageResponse = createResponseMessage(method.responseID, true, response);
|
|
645
646
|
requestQueue.resolveRequest(method.responseID, messageResponse);
|
|
646
647
|
completeMethodRequestContext(method);
|
|
@@ -470,6 +470,7 @@
|
|
|
470
470
|
"MessageType_DeviceSessionAskPin": 61202,
|
|
471
471
|
"MessageType_DeviceSessionAskPassphrase": 61203,
|
|
472
472
|
"MessageType_PortfolioUpdate": 61400,
|
|
473
|
+
"MessageType_NftUpdate": 61500,
|
|
473
474
|
"MessageType_OnboardingStatusGet": 61600,
|
|
474
475
|
"MessageType_OnboardingStatus": 61601
|
|
475
476
|
},
|
|
@@ -12200,26 +12201,11 @@
|
|
|
12200
12201
|
"DeviceSessionError_Busy": 5
|
|
12201
12202
|
}
|
|
12202
12203
|
},
|
|
12203
|
-
"DeviceSessionSeedDomain": {
|
|
12204
|
-
"values": {
|
|
12205
|
-
"SeedDomain_Standard": 1,
|
|
12206
|
-
"SeedDomain_Cardano": 2
|
|
12207
|
-
}
|
|
12208
|
-
},
|
|
12209
12204
|
"DeviceSessionGet": {
|
|
12210
12205
|
"fields": {
|
|
12211
12206
|
"session_id": {
|
|
12212
12207
|
"type": "bytes",
|
|
12213
12208
|
"id": 1
|
|
12214
|
-
},
|
|
12215
|
-
"btc_test_address": {
|
|
12216
|
-
"type": "string",
|
|
12217
|
-
"id": 2
|
|
12218
|
-
},
|
|
12219
|
-
"seed_domains": {
|
|
12220
|
-
"rule": "repeated",
|
|
12221
|
-
"type": "DeviceSessionSeedDomain",
|
|
12222
|
-
"id": 3
|
|
12223
12209
|
}
|
|
12224
12210
|
}
|
|
12225
12211
|
},
|
|
@@ -12553,6 +12539,15 @@
|
|
|
12553
12539
|
}
|
|
12554
12540
|
}
|
|
12555
12541
|
},
|
|
12542
|
+
"NftUpdate": {
|
|
12543
|
+
"fields": {
|
|
12544
|
+
"file_name_no_ext": {
|
|
12545
|
+
"rule": "required",
|
|
12546
|
+
"type": "string",
|
|
12547
|
+
"id": 1
|
|
12548
|
+
}
|
|
12549
|
+
}
|
|
12550
|
+
},
|
|
12556
12551
|
"OnboardingStep": {
|
|
12557
12552
|
"values": {
|
|
12558
12553
|
"ONBOARDING_STEP_UNKNOWN": 0,
|
package/src/inject.ts
CHANGED
|
@@ -162,6 +162,7 @@ export const createCoreApi = (
|
|
|
162
162
|
call({ ...params, connectId, method: 'deviceGetOnboardingStatus' }),
|
|
163
163
|
deviceUploadWallpaper: (connectId, params) =>
|
|
164
164
|
call({ ...params, connectId, method: 'deviceUploadWallpaper' }),
|
|
165
|
+
deviceUploadNft: (connectId, params) => call({ ...params, connectId, method: 'deviceUploadNft' }),
|
|
165
166
|
uploadPortfolio: (connectId, params) => call({ ...params, connectId, method: 'uploadPortfolio' }),
|
|
166
167
|
deviceRecovery: (connectId, params) => call({ ...params, connectId, method: 'deviceRecovery' }),
|
|
167
168
|
deviceReset: (connectId, params) => call({ ...params, connectId, method: 'deviceReset' }),
|
|
@@ -1,105 +1 @@
|
|
|
1
|
-
export type UnlockPolicy = 'none' | 'unlock-before-run'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Protocol V2 methods that are safe to replay after a locked response.
|
|
5
|
-
*
|
|
6
|
-
* Keep this as an exact allowlist. Name-pattern matching can silently make a new
|
|
7
|
-
* state-changing method replayable when it happens to share a suffix.
|
|
8
|
-
*/
|
|
9
|
-
export const PROTOCOL_V2_RETRY_ON_LOCKED_METHODS = [
|
|
10
|
-
'cipherKeyValue',
|
|
11
|
-
'allNetworkGetAddress',
|
|
12
|
-
'allNetworkGetAddressByLoop',
|
|
13
|
-
'btcGetAddress',
|
|
14
|
-
'btcGetPublicKey',
|
|
15
|
-
'btcSignMessage',
|
|
16
|
-
'btcSignPsbt',
|
|
17
|
-
'btcSignTransaction',
|
|
18
|
-
'btcVerifyMessage',
|
|
19
|
-
'confluxGetAddress',
|
|
20
|
-
'confluxSignMessage',
|
|
21
|
-
'confluxSignTransaction',
|
|
22
|
-
'evmGetAddress',
|
|
23
|
-
'evmGetPublicKey',
|
|
24
|
-
'evmSignMessage',
|
|
25
|
-
'evmSignTransaction',
|
|
26
|
-
'evmSignTypedData',
|
|
27
|
-
'evmVerifyMessage',
|
|
28
|
-
'starcoinGetAddress',
|
|
29
|
-
'starcoinGetPublicKey',
|
|
30
|
-
'starcoinSignMessage',
|
|
31
|
-
'starcoinSignTransaction',
|
|
32
|
-
'starcoinVerifyMessage',
|
|
33
|
-
'nemGetAddress',
|
|
34
|
-
'nemSignTransaction',
|
|
35
|
-
'solGetAddress',
|
|
36
|
-
'solSignTransaction',
|
|
37
|
-
'solSignOffchainMessage',
|
|
38
|
-
'solSignMessage',
|
|
39
|
-
'stellarGetAddress',
|
|
40
|
-
'stellarSignTransaction',
|
|
41
|
-
'tronGetAddress',
|
|
42
|
-
'tronSignMessage',
|
|
43
|
-
'tronSignTransaction',
|
|
44
|
-
'nearGetAddress',
|
|
45
|
-
'nearSignTransaction',
|
|
46
|
-
'aptosGetAddress',
|
|
47
|
-
'aptosGetPublicKey',
|
|
48
|
-
'aptosSignTransaction',
|
|
49
|
-
'aptosSignMessage',
|
|
50
|
-
'aptosSignInMessage',
|
|
51
|
-
'algoGetAddress',
|
|
52
|
-
'algoSignTransaction',
|
|
53
|
-
'cosmosGetAddress',
|
|
54
|
-
'cosmosGetPublicKey',
|
|
55
|
-
'cosmosSignTransaction',
|
|
56
|
-
'xrpGetAddress',
|
|
57
|
-
'xrpSignTransaction',
|
|
58
|
-
'suiGetAddress',
|
|
59
|
-
'suiGetPublicKey',
|
|
60
|
-
'suiSignMessage',
|
|
61
|
-
'suiSignTransaction',
|
|
62
|
-
'cardanoGetAddress',
|
|
63
|
-
'cardanoGetPublicKey',
|
|
64
|
-
'cardanoSignTransaction',
|
|
65
|
-
'cardanoSignMessage',
|
|
66
|
-
'filecoinGetAddress',
|
|
67
|
-
'filecoinSignTransaction',
|
|
68
|
-
'polkadotGetAddress',
|
|
69
|
-
'polkadotSignTransaction',
|
|
70
|
-
'kaspaGetAddress',
|
|
71
|
-
'kaspaSignTransaction',
|
|
72
|
-
'nexaGetAddress',
|
|
73
|
-
'nexaSignTransaction',
|
|
74
|
-
'nostrGetPublicKey',
|
|
75
|
-
'nostrSignEvent',
|
|
76
|
-
'nostrEncryptMessage',
|
|
77
|
-
'nostrDecryptMessage',
|
|
78
|
-
'nostrSignSchnorr',
|
|
79
|
-
'lnurlAuth',
|
|
80
|
-
'nervosGetAddress',
|
|
81
|
-
'nervosSignTransaction',
|
|
82
|
-
'dnxGetAddress',
|
|
83
|
-
'dnxSignTransaction',
|
|
84
|
-
'tonGetAddress',
|
|
85
|
-
'tonSignMessage',
|
|
86
|
-
'tonSignProof',
|
|
87
|
-
'tonSignData',
|
|
88
|
-
'scdoGetAddress',
|
|
89
|
-
'scdoSignTransaction',
|
|
90
|
-
'scdoSignMessage',
|
|
91
|
-
'alephiumGetAddress',
|
|
92
|
-
'alephiumSignTransaction',
|
|
93
|
-
'alephiumSignMessage',
|
|
94
|
-
'benfenGetAddress',
|
|
95
|
-
'benfenGetPublicKey',
|
|
96
|
-
'benfenSignMessage',
|
|
97
|
-
'benfenSignTransaction',
|
|
98
|
-
'neoGetAddress',
|
|
99
|
-
'neoSignTransaction',
|
|
100
|
-
] as const;
|
|
101
|
-
|
|
102
|
-
const retryOnLockedMethods = new Set<string>(PROTOCOL_V2_RETRY_ON_LOCKED_METHODS);
|
|
103
|
-
|
|
104
|
-
export const getProtocolV2UnlockPolicy = (methodName: string): UnlockPolicy =>
|
|
105
|
-
retryOnLockedMethods.has(methodName) ? 'retry-on-locked' : 'none';
|
|
1
|
+
export type UnlockPolicy = 'none' | 'unlock-before-run';
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import { LoggerNames, getLogger } from '../../utils';
|
|
4
|
+
import { isProtocolV2UiEnabled, resolveProtocolV2UiInteraction } from './uiInteraction';
|
|
5
|
+
import { refreshProtocolV2DeviceStatus } from './walletSession';
|
|
6
|
+
|
|
7
|
+
import type { BaseMethod } from '../../api/BaseMethod';
|
|
8
|
+
import type { Device } from '../../device/Device';
|
|
9
|
+
import type { HardwareUiInteractionMeta } from '../../events';
|
|
10
|
+
import type { ProtocolV2UiInteractionCoordinator } from './uiInteraction';
|
|
11
|
+
|
|
12
|
+
const Log = getLogger(LoggerNames.Core);
|
|
13
|
+
|
|
14
|
+
export type ProtocolV2UnlockContext = {
|
|
15
|
+
preflightCompleted: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const createProtocolV2UnlockContext = (): ProtocolV2UnlockContext => ({
|
|
19
|
+
preflightCompleted: false,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
type RunnableMethod = Pick<
|
|
23
|
+
BaseMethod,
|
|
24
|
+
| 'run'
|
|
25
|
+
| 'unlockPolicy'
|
|
26
|
+
| 'useDevicePassphraseState'
|
|
27
|
+
| 'protocolV2UiInteraction'
|
|
28
|
+
| 'protocolV2UiMode'
|
|
29
|
+
| 'params'
|
|
30
|
+
| 'payload'
|
|
31
|
+
> & {
|
|
32
|
+
name?: string;
|
|
33
|
+
protocolV2UnlockContext?: ProtocolV2UnlockContext;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type UiInteractionCoordinator = Pick<
|
|
37
|
+
ProtocolV2UiInteractionCoordinator,
|
|
38
|
+
'enterMethodInteraction' | 'enterUnlockInteraction'
|
|
39
|
+
>;
|
|
40
|
+
|
|
41
|
+
type RunMethodWithUnlockPolicyOptions<T> = {
|
|
42
|
+
context?: ProtocolV2UnlockContext;
|
|
43
|
+
uiCoordinator?: UiInteractionCoordinator;
|
|
44
|
+
afterStatusBeforeUnlock?: () => void | Promise<void>;
|
|
45
|
+
prepare?: () => Promise<void>;
|
|
46
|
+
run?: () => Promise<T>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export async function runMethodWithUnlockPolicy<T = unknown>(
|
|
50
|
+
method: RunnableMethod,
|
|
51
|
+
device: Device,
|
|
52
|
+
options: RunMethodWithUnlockPolicyOptions<T> = {}
|
|
53
|
+
): Promise<T> {
|
|
54
|
+
const {
|
|
55
|
+
context = createProtocolV2UnlockContext(),
|
|
56
|
+
uiCoordinator,
|
|
57
|
+
afterStatusBeforeUnlock,
|
|
58
|
+
prepare,
|
|
59
|
+
run: configuredRun,
|
|
60
|
+
} = options;
|
|
61
|
+
const run = configuredRun ?? (() => method.run() as Promise<T>);
|
|
62
|
+
method.protocolV2UnlockContext = context;
|
|
63
|
+
|
|
64
|
+
const shouldEmitUi = isProtocolV2UiEnabled(method);
|
|
65
|
+
const shouldCoordinateUi = shouldEmitUi && uiCoordinator !== undefined;
|
|
66
|
+
const requiresPreUnlock =
|
|
67
|
+
device.isProtocolV2() &&
|
|
68
|
+
(method.useDevicePassphraseState || method.unlockPolicy === 'unlock-before-run') &&
|
|
69
|
+
!device.isBootloader?.() &&
|
|
70
|
+
!device.isRomloader?.();
|
|
71
|
+
|
|
72
|
+
if (requiresPreUnlock && !context.preflightCompleted) {
|
|
73
|
+
const status = await refreshProtocolV2DeviceStatus(device);
|
|
74
|
+
if (typeof status?.unlocked !== 'boolean') {
|
|
75
|
+
throw ERRORS.TypedError(
|
|
76
|
+
HardwareErrorCode.RuntimeError,
|
|
77
|
+
'Protocol V2 DeviceStatus did not report an explicit unlock state.'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
await afterStatusBeforeUnlock?.();
|
|
82
|
+
|
|
83
|
+
if (!status.unlocked) {
|
|
84
|
+
const unlockInteraction: HardwareUiInteractionMeta | undefined = shouldCoordinateUi
|
|
85
|
+
? uiCoordinator.enterUnlockInteraction(method.name)
|
|
86
|
+
: undefined;
|
|
87
|
+
const unlockedStatus = await device.unlockDevice(
|
|
88
|
+
undefined,
|
|
89
|
+
shouldCoordinateUi
|
|
90
|
+
? { emitUiEvent: false, interaction: unlockInteraction }
|
|
91
|
+
: {
|
|
92
|
+
source: 'unlock-coordinator',
|
|
93
|
+
reason: 'device-locked',
|
|
94
|
+
deviceOnly: true,
|
|
95
|
+
method: method.name,
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
if (unlockedStatus?.unlocked !== true) {
|
|
99
|
+
throw ERRORS.TypedError(
|
|
100
|
+
HardwareErrorCode.RuntimeError,
|
|
101
|
+
'Protocol V2 device remained locked after the unlock flow.'
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
Log.debug('Protocol V2 pre-unlock completed', { method: method.name });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
context.preflightCompleted = true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
await prepare?.();
|
|
111
|
+
|
|
112
|
+
if (shouldCoordinateUi) {
|
|
113
|
+
uiCoordinator.enterMethodInteraction(resolveProtocolV2UiInteraction(method));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return run();
|
|
117
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
-
import { DeviceSessionPinType
|
|
2
|
+
import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
|
|
3
3
|
|
|
4
4
|
import { DEVICE } from '../../events';
|
|
5
5
|
import { assertCompleteDeviceSession } from './deviceSession';
|
|
@@ -60,34 +60,15 @@ const negotiateEventlessWalletSession = async (device: Device) => {
|
|
|
60
60
|
await device.ensureProtocolV2RuntimeContext();
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
const getDeviceSession = async (
|
|
64
|
-
device
|
|
65
|
-
request: DeviceSessionGet,
|
|
66
|
-
deriveCardano?: boolean
|
|
67
|
-
) => {
|
|
68
|
-
const payload =
|
|
69
|
-
deriveCardano === undefined
|
|
70
|
-
? request
|
|
71
|
-
: {
|
|
72
|
-
...request,
|
|
73
|
-
seed_domains: [
|
|
74
|
-
DeviceSessionSeedDomain.SeedDomain_Standard,
|
|
75
|
-
...(deriveCardano ? [DeviceSessionSeedDomain.SeedDomain_Cardano] : []),
|
|
76
|
-
],
|
|
77
|
-
};
|
|
78
|
-
return device.commands.typedCall('DeviceSessionGet', 'DeviceSession', payload);
|
|
79
|
-
};
|
|
63
|
+
const getDeviceSession = async (device: Device, request: DeviceSessionGet) =>
|
|
64
|
+
device.commands.typedCall('DeviceSessionGet', 'DeviceSession', request);
|
|
80
65
|
|
|
81
66
|
const askDevicePassphrase = async (device: Device, requestPayload: DeviceSessionAskPassphrase) => {
|
|
82
67
|
await device.commands.typedCall('DeviceSessionAskPassphrase', 'Success', requestPayload);
|
|
83
68
|
await refreshProtocolV2DeviceStatus(device);
|
|
84
69
|
};
|
|
85
70
|
|
|
86
|
-
const selectDeviceSession = async (
|
|
87
|
-
device: Device,
|
|
88
|
-
expectedPassphraseState?: string,
|
|
89
|
-
deriveCardano?: boolean
|
|
90
|
-
) => {
|
|
71
|
+
const selectDeviceSession = async (device: Device, expectedPassphraseState?: string) => {
|
|
91
72
|
const existsAttachPinUser = device.features?.attachToPinEnabled === true;
|
|
92
73
|
const metadata = {
|
|
93
74
|
source: 'wallet-session-coordinator' as const,
|
|
@@ -148,7 +129,7 @@ const selectDeviceSession = async (
|
|
|
148
129
|
emitUiEvent: false,
|
|
149
130
|
interaction: attachPinInteraction,
|
|
150
131
|
});
|
|
151
|
-
return getDeviceSession(device, {}
|
|
132
|
+
return getDeviceSession(device, {});
|
|
152
133
|
}
|
|
153
134
|
|
|
154
135
|
if (hasHostPassphrase) {
|
|
@@ -156,7 +137,7 @@ const selectDeviceSession = async (
|
|
|
156
137
|
passphrase: hostPassphrase,
|
|
157
138
|
on_device: false,
|
|
158
139
|
});
|
|
159
|
-
return getDeviceSession(device, {}
|
|
140
|
+
return getDeviceSession(device, {});
|
|
160
141
|
}
|
|
161
142
|
|
|
162
143
|
device.emit(DEVICE.PASSPHRASE_ON_DEVICE, device, {
|
|
@@ -164,7 +145,7 @@ const selectDeviceSession = async (
|
|
|
164
145
|
...(passphraseInteraction ? { interaction: passphraseInteraction } : {}),
|
|
165
146
|
});
|
|
166
147
|
await askDevicePassphrase(device, { on_device: true });
|
|
167
|
-
return getDeviceSession(device, {}
|
|
148
|
+
return getDeviceSession(device, {});
|
|
168
149
|
};
|
|
169
150
|
|
|
170
151
|
export async function getProtocolV2WalletSession(
|
|
@@ -253,14 +234,9 @@ export async function getProtocolV2WalletSession(
|
|
|
253
234
|
if (options?.selectMainWalletBeforeRestore) {
|
|
254
235
|
await selectMainWallet();
|
|
255
236
|
}
|
|
256
|
-
response = await getDeviceSession(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
session_id: cachedStandardSession.sessionId,
|
|
260
|
-
btc_test_address: cachedStandardSession.passphraseState,
|
|
261
|
-
},
|
|
262
|
-
options?.deriveCardano
|
|
263
|
-
);
|
|
237
|
+
response = await getDeviceSession(device, {
|
|
238
|
+
session_id: cachedStandardSession.sessionId,
|
|
239
|
+
});
|
|
264
240
|
resumed = true;
|
|
265
241
|
} catch (error) {
|
|
266
242
|
device.clearStandardInternalState?.();
|
|
@@ -273,18 +249,13 @@ export async function getProtocolV2WalletSession(
|
|
|
273
249
|
|
|
274
250
|
if (!response) {
|
|
275
251
|
await selectMainWallet();
|
|
276
|
-
response = await getDeviceSession(device, {}
|
|
252
|
+
response = await getDeviceSession(device, {});
|
|
277
253
|
}
|
|
278
254
|
} else if (cachedSessionId && expectedPassphraseState) {
|
|
279
255
|
try {
|
|
280
|
-
response = await getDeviceSession(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
session_id: cachedSessionId,
|
|
284
|
-
btc_test_address: expectedPassphraseState,
|
|
285
|
-
},
|
|
286
|
-
options?.deriveCardano
|
|
287
|
-
);
|
|
256
|
+
response = await getDeviceSession(device, {
|
|
257
|
+
session_id: cachedSessionId,
|
|
258
|
+
});
|
|
288
259
|
resumed = true;
|
|
289
260
|
} catch (error) {
|
|
290
261
|
device.clearInternalState();
|
|
@@ -295,11 +266,7 @@ export async function getProtocolV2WalletSession(
|
|
|
295
266
|
}
|
|
296
267
|
} else if (expectedPassphraseState) {
|
|
297
268
|
try {
|
|
298
|
-
response = await getDeviceSession(
|
|
299
|
-
device,
|
|
300
|
-
{ btc_test_address: expectedPassphraseState },
|
|
301
|
-
options?.deriveCardano
|
|
302
|
-
);
|
|
269
|
+
response = await getDeviceSession(device, {});
|
|
303
270
|
} catch (error) {
|
|
304
271
|
if (options?.resumeOnly || !isWalletSessionInvalidError(error)) {
|
|
305
272
|
throw error;
|
|
@@ -312,7 +279,7 @@ export async function getProtocolV2WalletSession(
|
|
|
312
279
|
device.clearInternalState();
|
|
313
280
|
throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
|
|
314
281
|
}
|
|
315
|
-
response = await selectDeviceSession(device, expectedPassphraseState
|
|
282
|
+
response = await selectDeviceSession(device, expectedPassphraseState);
|
|
316
283
|
}
|
|
317
284
|
|
|
318
285
|
let { message } = response;
|
|
@@ -336,10 +303,10 @@ export async function getProtocolV2WalletSession(
|
|
|
336
303
|
if (options?.onlyMainPin) {
|
|
337
304
|
device.clearStandardInternalState?.();
|
|
338
305
|
await selectMainWallet(true);
|
|
339
|
-
response = await getDeviceSession(device, {}
|
|
306
|
+
response = await getDeviceSession(device, {});
|
|
340
307
|
} else {
|
|
341
308
|
device.clearInternalState();
|
|
342
|
-
response = await selectDeviceSession(device, expectedPassphraseState
|
|
309
|
+
response = await selectDeviceSession(device, expectedPassphraseState);
|
|
343
310
|
}
|
|
344
311
|
message = response.message;
|
|
345
312
|
try {
|
package/src/types/api/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
deviceGetOnboardingStatus,
|
|
3
3
|
deviceReboot,
|
|
4
|
+
deviceUploadNft,
|
|
4
5
|
deviceUploadWallpaper,
|
|
5
6
|
testProtocolV2Ping,
|
|
6
7
|
uploadPortfolio,
|
|
@@ -234,6 +235,7 @@ export type CoreApi = {
|
|
|
234
235
|
*/
|
|
235
236
|
deviceReboot: typeof deviceReboot;
|
|
236
237
|
deviceGetOnboardingStatus: typeof deviceGetOnboardingStatus;
|
|
238
|
+
deviceUploadNft: typeof deviceUploadNft;
|
|
237
239
|
deviceUploadWallpaper: typeof deviceUploadWallpaper;
|
|
238
240
|
uploadPortfolio: typeof uploadPortfolio;
|
|
239
241
|
|