@onekeyfe/hd-core 1.2.2-alpha.9 → 1.2.3-alpha.1
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__/DeviceCommands.test.ts +254 -1
- package/__tests__/device-lifecycle-events.test.ts +46 -14
- package/__tests__/logBlockEvent.test.ts +45 -122
- package/__tests__/open-wallet-session.test.ts +53 -0
- package/__tests__/protocol-v2.test.ts +79 -0
- package/__tests__/sol-sign-offchain-message.test.ts +72 -0
- package/dist/api/FirmwareUpdateV4.d.ts +1 -0
- package/dist/api/FirmwareUpdateV4.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/solana/SolSignOffchainMessage.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/uiPromiseRegistry.d.ts +1 -1
- package/dist/device/DeviceCommands.d.ts +5 -4
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +19 -12
- package/dist/index.js +138 -130
- package/dist/protocols/protocol-v2/walletSession.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/utils/patch.d.ts +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +9 -4
- package/src/api/UploadPortfolio.ts +5 -4
- package/src/api/solana/SolSignOffchainMessage.ts +44 -4
- package/src/core/index.ts +24 -9
- package/src/data/messages/messages-protocol-v2.json +28 -33
- package/src/data/messages/messages.json +8 -5
- package/src/device/DeviceCommands.ts +29 -2
- package/src/events/logBlockEvent.ts +6 -75
- package/src/protocols/protocol-v2/walletSession.ts +7 -2
- package/src/types/api/protocolV2.ts +3 -4
- package/src/types/api/solSignOffchainMessage.ts +2 -0
|
@@ -4463,10 +4463,6 @@
|
|
|
4463
4463
|
"request_id": {
|
|
4464
4464
|
"type": "bytes",
|
|
4465
4465
|
"id": 5
|
|
4466
|
-
},
|
|
4467
|
-
"source_fingerprint": {
|
|
4468
|
-
"type": "uint32",
|
|
4469
|
-
"id": 6
|
|
4470
4466
|
}
|
|
4471
4467
|
}
|
|
4472
4468
|
},
|
|
@@ -4722,10 +4718,6 @@
|
|
|
4722
4718
|
"expected_address": {
|
|
4723
4719
|
"type": "bytes",
|
|
4724
4720
|
"id": 12
|
|
4725
|
-
},
|
|
4726
|
-
"source_fingerprint": {
|
|
4727
|
-
"type": "uint32",
|
|
4728
|
-
"id": 13
|
|
4729
4721
|
}
|
|
4730
4722
|
}
|
|
4731
4723
|
},
|
|
@@ -4810,10 +4802,6 @@
|
|
|
4810
4802
|
"expected_address": {
|
|
4811
4803
|
"type": "bytes",
|
|
4812
4804
|
"id": 12
|
|
4813
|
-
},
|
|
4814
|
-
"source_fingerprint": {
|
|
4815
|
-
"type": "uint32",
|
|
4816
|
-
"id": 13
|
|
4817
4805
|
}
|
|
4818
4806
|
}
|
|
4819
4807
|
},
|
|
@@ -4961,7 +4949,14 @@
|
|
|
4961
4949
|
"type": "EthereumAuthorizationSignature",
|
|
4962
4950
|
"id": 10
|
|
4963
4951
|
}
|
|
4964
|
-
}
|
|
4952
|
+
},
|
|
4953
|
+
"reserved": [
|
|
4954
|
+
[5, 5],
|
|
4955
|
+
[6, 6],
|
|
4956
|
+
[7, 7],
|
|
4957
|
+
[8, 8],
|
|
4958
|
+
[9, 9]
|
|
4959
|
+
]
|
|
4965
4960
|
},
|
|
4966
4961
|
"EthereumTxAckOneKey": {
|
|
4967
4962
|
"fields": {
|
|
@@ -4990,10 +4985,6 @@
|
|
|
4990
4985
|
"chain_id": {
|
|
4991
4986
|
"type": "uint64",
|
|
4992
4987
|
"id": 3
|
|
4993
|
-
},
|
|
4994
|
-
"source_fingerprint": {
|
|
4995
|
-
"type": "uint32",
|
|
4996
|
-
"id": 4
|
|
4997
4988
|
}
|
|
4998
4989
|
}
|
|
4999
4990
|
},
|
|
@@ -8968,7 +8959,8 @@
|
|
|
8968
8959
|
},
|
|
8969
8960
|
"SolanaOffChainMessageVersion": {
|
|
8970
8961
|
"values": {
|
|
8971
|
-
"MESSAGE_VERSION_0": 0
|
|
8962
|
+
"MESSAGE_VERSION_0": 0,
|
|
8963
|
+
"MESSAGE_VERSION_1": 1
|
|
8972
8964
|
}
|
|
8973
8965
|
},
|
|
8974
8966
|
"SolanaOffChainMessageFormat": {
|
|
@@ -9016,10 +9008,6 @@
|
|
|
9016
9008
|
"rule": "required",
|
|
9017
9009
|
"type": "bytes",
|
|
9018
9010
|
"id": 2
|
|
9019
|
-
},
|
|
9020
|
-
"source_fingerprint": {
|
|
9021
|
-
"type": "uint32",
|
|
9022
|
-
"id": 3
|
|
9023
9011
|
}
|
|
9024
9012
|
}
|
|
9025
9013
|
},
|
|
@@ -9056,17 +9044,15 @@
|
|
|
9056
9044
|
},
|
|
9057
9045
|
"message_format": {
|
|
9058
9046
|
"type": "SolanaOffChainMessageFormat",
|
|
9059
|
-
"id": 4
|
|
9060
|
-
"options": {
|
|
9061
|
-
"default": "V0_RESTRICTED_ASCII"
|
|
9062
|
-
}
|
|
9047
|
+
"id": 4
|
|
9063
9048
|
},
|
|
9064
9049
|
"application_domain": {
|
|
9065
9050
|
"type": "bytes",
|
|
9066
9051
|
"id": 5
|
|
9067
9052
|
},
|
|
9068
|
-
"
|
|
9069
|
-
"
|
|
9053
|
+
"required_signers": {
|
|
9054
|
+
"rule": "repeated",
|
|
9055
|
+
"type": "bytes",
|
|
9070
9056
|
"id": 6
|
|
9071
9057
|
}
|
|
9072
9058
|
}
|
|
@@ -9085,10 +9071,6 @@
|
|
|
9085
9071
|
"rule": "required",
|
|
9086
9072
|
"type": "bytes",
|
|
9087
9073
|
"id": 2
|
|
9088
|
-
},
|
|
9089
|
-
"source_fingerprint": {
|
|
9090
|
-
"type": "uint32",
|
|
9091
|
-
"id": 3
|
|
9092
9074
|
}
|
|
9093
9075
|
}
|
|
9094
9076
|
},
|
|
@@ -11885,6 +11867,10 @@
|
|
|
11885
11867
|
"manufacture_time": {
|
|
11886
11868
|
"type": "DeviceFactoryInfoManufactureTime",
|
|
11887
11869
|
"id": 5
|
|
11870
|
+
},
|
|
11871
|
+
"data": {
|
|
11872
|
+
"type": "bytes",
|
|
11873
|
+
"id": 100
|
|
11888
11874
|
}
|
|
11889
11875
|
}
|
|
11890
11876
|
},
|
|
@@ -11894,11 +11880,20 @@
|
|
|
11894
11880
|
"rule": "required",
|
|
11895
11881
|
"type": "DeviceFactoryInfo",
|
|
11896
11882
|
"id": 1
|
|
11883
|
+
},
|
|
11884
|
+
"full_data": {
|
|
11885
|
+
"type": "bool",
|
|
11886
|
+
"id": 2
|
|
11897
11887
|
}
|
|
11898
11888
|
}
|
|
11899
11889
|
},
|
|
11900
11890
|
"DeviceFactoryInfoGet": {
|
|
11901
|
-
"fields": {
|
|
11891
|
+
"fields": {
|
|
11892
|
+
"full_data": {
|
|
11893
|
+
"type": "bool",
|
|
11894
|
+
"id": 1
|
|
11895
|
+
}
|
|
11896
|
+
}
|
|
11902
11897
|
},
|
|
11903
11898
|
"DeviceFactoryPermanentLock": {
|
|
11904
11899
|
"fields": {
|
|
@@ -10243,7 +10243,8 @@
|
|
|
10243
10243
|
},
|
|
10244
10244
|
"SolanaOffChainMessageVersion": {
|
|
10245
10245
|
"values": {
|
|
10246
|
-
"MESSAGE_VERSION_0": 0
|
|
10246
|
+
"MESSAGE_VERSION_0": 0,
|
|
10247
|
+
"MESSAGE_VERSION_1": 1
|
|
10247
10248
|
}
|
|
10248
10249
|
},
|
|
10249
10250
|
"SolanaOffChainMessageFormat": {
|
|
@@ -10276,14 +10277,16 @@
|
|
|
10276
10277
|
},
|
|
10277
10278
|
"message_format": {
|
|
10278
10279
|
"type": "SolanaOffChainMessageFormat",
|
|
10279
|
-
"id": 4
|
|
10280
|
-
"options": {
|
|
10281
|
-
"default": "V0_RESTRICTED_ASCII"
|
|
10282
|
-
}
|
|
10280
|
+
"id": 4
|
|
10283
10281
|
},
|
|
10284
10282
|
"application_domain": {
|
|
10285
10283
|
"type": "bytes",
|
|
10286
10284
|
"id": 5
|
|
10285
|
+
},
|
|
10286
|
+
"required_signers": {
|
|
10287
|
+
"rule": "repeated",
|
|
10288
|
+
"type": "bytes",
|
|
10289
|
+
"id": 7
|
|
10287
10290
|
}
|
|
10288
10291
|
}
|
|
10289
10292
|
},
|
|
@@ -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
|
|
|
@@ -8,8 +8,9 @@ export const LogBlockEvent: Set<string> = new Set([
|
|
|
8
8
|
UI_RESPONSE.RECEIVE_PASSPHRASE,
|
|
9
9
|
]);
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
// These resource/file APIs did not exist in 1.1.32. Skip the payload so
|
|
12
|
+
// the log layer does not copy huge Base64 or binary data.
|
|
13
|
+
const LogPayloadBlockMethod: Set<string> = new Set([
|
|
13
14
|
'deviceUploadNft',
|
|
14
15
|
'deviceUploadWallpaper',
|
|
15
16
|
'uploadPortfolio',
|
|
@@ -17,61 +18,6 @@ const LogLabelMethod: Set<string> = new Set([
|
|
|
17
18
|
'fileRead',
|
|
18
19
|
]);
|
|
19
20
|
|
|
20
|
-
// 资源上传参数可能包含很大的 Base64 字符串。这里按方法整段跳过,避免日志层
|
|
21
|
-
// 递归复制和序列化这些数据;资源 API 与传输内容本身保持不变。
|
|
22
|
-
const LogPayloadBlockMethod: Set<string> = new Set([
|
|
23
|
-
'deviceUploadNft',
|
|
24
|
-
'deviceUploadWallpaper',
|
|
25
|
-
'uploadPortfolio',
|
|
26
|
-
]);
|
|
27
|
-
|
|
28
|
-
const SensitiveLogKeys: Set<string> = new Set([
|
|
29
|
-
'devicestate',
|
|
30
|
-
'entropy',
|
|
31
|
-
'expectedpassphrasestate',
|
|
32
|
-
'mnemonic',
|
|
33
|
-
'passphrase',
|
|
34
|
-
'passphrasestate',
|
|
35
|
-
'password',
|
|
36
|
-
'pin',
|
|
37
|
-
'privatekey',
|
|
38
|
-
'seed',
|
|
39
|
-
'session',
|
|
40
|
-
'sessionid',
|
|
41
|
-
'walletsessionid',
|
|
42
|
-
'xprv',
|
|
43
|
-
]);
|
|
44
|
-
|
|
45
|
-
const normalizeLogKey = (key: string) => key.replace(/[_-]/g, '').toLowerCase();
|
|
46
|
-
|
|
47
|
-
const isSigningMethod = (methodName: string) => /sign/i.test(methodName);
|
|
48
|
-
|
|
49
|
-
const redactLogValue = (value: unknown, seen: WeakSet<object>): unknown => {
|
|
50
|
-
if (ArrayBuffer.isView(value)) {
|
|
51
|
-
return `[BINARY:${value.byteLength}]`;
|
|
52
|
-
}
|
|
53
|
-
if (value instanceof ArrayBuffer) {
|
|
54
|
-
return `[BINARY:${value.byteLength}]`;
|
|
55
|
-
}
|
|
56
|
-
if (Array.isArray(value)) {
|
|
57
|
-
return value.map(item => redactLogValue(item, seen));
|
|
58
|
-
}
|
|
59
|
-
if (!value || typeof value !== 'object') return value;
|
|
60
|
-
if (seen.has(value)) return '[CIRCULAR]';
|
|
61
|
-
|
|
62
|
-
seen.add(value);
|
|
63
|
-
const redacted = Object.fromEntries(
|
|
64
|
-
Object.entries(value as Record<string, unknown>).map(([key, item]) => [
|
|
65
|
-
key,
|
|
66
|
-
SensitiveLogKeys.has(normalizeLogKey(key)) && item !== null && item !== undefined
|
|
67
|
-
? '[REDACTED]'
|
|
68
|
-
: redactLogValue(item, seen),
|
|
69
|
-
])
|
|
70
|
-
);
|
|
71
|
-
seen.delete(value);
|
|
72
|
-
return redacted;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
21
|
export function getLogBlockLabel(message: unknown): string | undefined {
|
|
76
22
|
if (!message || typeof message !== 'object') return undefined;
|
|
77
23
|
|
|
@@ -86,7 +32,7 @@ export function getLogBlockLabel(message: unknown): string | undefined {
|
|
|
86
32
|
}
|
|
87
33
|
|
|
88
34
|
const methodName = method ?? payload?.method;
|
|
89
|
-
if (methodName &&
|
|
35
|
+
if (methodName && LogPayloadBlockMethod.has(methodName)) {
|
|
90
36
|
return methodName;
|
|
91
37
|
}
|
|
92
38
|
|
|
@@ -94,25 +40,10 @@ export function getLogBlockLabel(message: unknown): string | undefined {
|
|
|
94
40
|
}
|
|
95
41
|
|
|
96
42
|
export function getSafeLogPayload(value: unknown, blockLabel?: string): unknown {
|
|
97
|
-
if (
|
|
98
|
-
blockLabel &&
|
|
99
|
-
(LogBlockEvent.has(blockLabel) ||
|
|
100
|
-
LogPayloadBlockMethod.has(blockLabel) ||
|
|
101
|
-
isSigningMethod(blockLabel))
|
|
102
|
-
) {
|
|
43
|
+
if (blockLabel) {
|
|
103
44
|
return { method: blockLabel, payload: '[REDACTED]' };
|
|
104
45
|
}
|
|
105
|
-
|
|
106
|
-
const redactedValue = redactLogValue(value, new WeakSet());
|
|
107
|
-
if (
|
|
108
|
-
blockLabel &&
|
|
109
|
-
redactedValue &&
|
|
110
|
-
typeof redactedValue === 'object' &&
|
|
111
|
-
!Array.isArray(redactedValue)
|
|
112
|
-
) {
|
|
113
|
-
return { ...redactedValue, method: blockLabel };
|
|
114
|
-
}
|
|
115
|
-
return redactedValue;
|
|
46
|
+
return value;
|
|
116
47
|
}
|
|
117
48
|
|
|
118
49
|
export function formatLogMethodLabel(label: string, methodName?: string): string {
|
|
@@ -234,9 +234,9 @@ export async function getProtocolV2WalletSession(
|
|
|
234
234
|
const forceWalletSelection =
|
|
235
235
|
options?.forceWalletSelection === true || options?.initSession === true;
|
|
236
236
|
const readCurrentAttachPinSession = options?.readCurrentAttachPinSession === true;
|
|
237
|
-
const sessionIsAttachPinWallet = (session?:
|
|
237
|
+
const sessionIsAttachPinWallet = (session?: unknown) =>
|
|
238
238
|
readCurrentAttachPinSession ||
|
|
239
|
-
session?.viaAttachPin === true ||
|
|
239
|
+
(session as { viaAttachPin?: boolean } | undefined)?.viaAttachPin === true ||
|
|
240
240
|
device.features?.unlockedAttachPin === true;
|
|
241
241
|
|
|
242
242
|
if (forceWalletSelection) {
|
|
@@ -455,6 +455,11 @@ export async function getProtocolV2WalletSession(
|
|
|
455
455
|
throw error;
|
|
456
456
|
}
|
|
457
457
|
resumed = false;
|
|
458
|
+
if (device.features?.unlockedAttachPin === true) {
|
|
459
|
+
// Locking invalidates the old handle, but Attach PIN already selected the wallet.
|
|
460
|
+
// Read the current session and validate its passphrase state below.
|
|
461
|
+
response = await getDeviceSession(device, sessionGetRequest());
|
|
462
|
+
}
|
|
458
463
|
}
|
|
459
464
|
} else if (expectedPassphraseState) {
|
|
460
465
|
try {
|
|
@@ -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(
|