@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.
Files changed (37) hide show
  1. package/__tests__/DeviceCommands.test.ts +254 -1
  2. package/__tests__/device-lifecycle-events.test.ts +46 -14
  3. package/__tests__/logBlockEvent.test.ts +45 -122
  4. package/__tests__/open-wallet-session.test.ts +53 -0
  5. package/__tests__/protocol-v2.test.ts +79 -0
  6. package/__tests__/sol-sign-offchain-message.test.ts +72 -0
  7. package/dist/api/FirmwareUpdateV4.d.ts +1 -0
  8. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  9. package/dist/api/UploadPortfolio.d.ts +1 -0
  10. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  11. package/dist/api/solana/SolSignOffchainMessage.d.ts.map +1 -1
  12. package/dist/core/index.d.ts +2 -1
  13. package/dist/core/index.d.ts.map +1 -1
  14. package/dist/core/uiPromiseRegistry.d.ts +1 -1
  15. package/dist/device/DeviceCommands.d.ts +5 -4
  16. package/dist/device/DeviceCommands.d.ts.map +1 -1
  17. package/dist/events/logBlockEvent.d.ts.map +1 -1
  18. package/dist/index.d.ts +19 -12
  19. package/dist/index.js +138 -130
  20. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  21. package/dist/types/api/protocolV2.d.ts +3 -4
  22. package/dist/types/api/protocolV2.d.ts.map +1 -1
  23. package/dist/types/api/solSignOffchainMessage.d.ts +1 -0
  24. package/dist/types/api/solSignOffchainMessage.d.ts.map +1 -1
  25. package/dist/utils/patch.d.ts +1 -1
  26. package/package.json +4 -4
  27. package/src/api/FirmwareUpdateV4.ts +9 -4
  28. package/src/api/UploadPortfolio.ts +5 -4
  29. package/src/api/solana/SolSignOffchainMessage.ts +44 -4
  30. package/src/core/index.ts +24 -9
  31. package/src/data/messages/messages-protocol-v2.json +28 -33
  32. package/src/data/messages/messages.json +8 -5
  33. package/src/device/DeviceCommands.ts +29 -2
  34. package/src/events/logBlockEvent.ts +6 -75
  35. package/src/protocols/protocol-v2/walletSession.ts +7 -2
  36. package/src/types/api/protocolV2.ts +3 -4
  37. 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
- "source_fingerprint": {
9069
- "type": "uint32",
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 resp = await this.call(type, msg, options);
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
- const LogLabelMethod: Set<string> = new Set([
12
- 'openWalletSession',
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 && (LogLabelMethod.has(methodName) || isSigningMethod(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?: { viaAttachPin?: boolean }) =>
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(