@onekeyfe/hd-core 1.1.26-alpha.2 → 1.1.26-alpha.30

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 (158) hide show
  1. package/__tests__/protocol-v2.test.ts +912 -0
  2. package/dist/api/DirList.d.ts +10 -0
  3. package/dist/api/DirList.d.ts.map +1 -0
  4. package/dist/api/DirMake.d.ts +9 -0
  5. package/dist/api/DirMake.d.ts.map +1 -0
  6. package/dist/api/DirRemove.d.ts +9 -0
  7. package/dist/api/DirRemove.d.ts.map +1 -0
  8. package/dist/api/FileDelete.d.ts +9 -0
  9. package/dist/api/FileDelete.d.ts.map +1 -0
  10. package/dist/api/FileRead.d.ts +19 -0
  11. package/dist/api/FileRead.d.ts.map +1 -0
  12. package/dist/api/FileWrite.d.ts +23 -0
  13. package/dist/api/FileWrite.d.ts.map +1 -0
  14. package/dist/api/FirmwareUpdateV3.d.ts +1 -0
  15. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  16. package/dist/api/FirmwareUpdateV4.d.ts +32 -0
  17. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -0
  18. package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
  19. package/dist/api/PathInfo.d.ts +9 -0
  20. package/dist/api/PathInfo.d.ts.map +1 -0
  21. package/dist/api/SearchDevices.d.ts +2 -1
  22. package/dist/api/SearchDevices.d.ts.map +1 -1
  23. package/dist/api/alephium/AlephiumSignTransaction.d.ts +1 -1
  24. package/dist/api/alephium/AlephiumSignTransaction.d.ts.map +1 -1
  25. package/dist/api/device/DeviceRebootToBoardloader.d.ts +1 -1
  26. package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
  27. package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
  28. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +10 -2
  29. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  30. package/dist/api/index.d.ts +27 -0
  31. package/dist/api/index.d.ts.map +1 -1
  32. package/dist/api/protocol-v2/DevFirmwareUpdate.d.ts +7 -0
  33. package/dist/api/protocol-v2/DevFirmwareUpdate.d.ts.map +1 -0
  34. package/dist/api/protocol-v2/DevGetDeviceInfo.d.ts +7 -0
  35. package/dist/api/protocol-v2/DevGetDeviceInfo.d.ts.map +1 -0
  36. package/dist/api/protocol-v2/DevGetFirmwareUpdateStatus.d.ts +6 -0
  37. package/dist/api/protocol-v2/DevGetFirmwareUpdateStatus.d.ts.map +1 -0
  38. package/dist/api/protocol-v2/DevGetOnboardingStatus.d.ts +6 -0
  39. package/dist/api/protocol-v2/DevGetOnboardingStatus.d.ts.map +1 -0
  40. package/dist/api/protocol-v2/DevReboot.d.ts +7 -0
  41. package/dist/api/protocol-v2/DevReboot.d.ts.map +1 -0
  42. package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts +7 -0
  43. package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts.map +1 -0
  44. package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts +6 -0
  45. package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts.map +1 -0
  46. package/dist/api/protocol-v2/FilesystemFixPermission.d.ts +6 -0
  47. package/dist/api/protocol-v2/FilesystemFixPermission.d.ts.map +1 -0
  48. package/dist/api/protocol-v2/FilesystemFormat.d.ts +6 -0
  49. package/dist/api/protocol-v2/FilesystemFormat.d.ts.map +1 -0
  50. package/dist/api/protocol-v2/GetProtoVersion.d.ts +6 -0
  51. package/dist/api/protocol-v2/GetProtoVersion.d.ts.map +1 -0
  52. package/dist/api/protocol-v2/Ping.d.ts +8 -0
  53. package/dist/api/protocol-v2/Ping.d.ts.map +1 -0
  54. package/dist/api/protocol-v2/helpers.d.ts +49 -0
  55. package/dist/api/protocol-v2/helpers.d.ts.map +1 -0
  56. package/dist/api/ton/TonSignData.d.ts +7 -0
  57. package/dist/api/ton/TonSignData.d.ts.map +1 -0
  58. package/dist/constants/index.d.ts +1 -1
  59. package/dist/constants/index.d.ts.map +1 -1
  60. package/dist/core/index.d.ts.map +1 -1
  61. package/dist/data-manager/DataManager.d.ts +4 -2
  62. package/dist/data-manager/DataManager.d.ts.map +1 -1
  63. package/dist/data-manager/TransportManager.d.ts +2 -1
  64. package/dist/data-manager/TransportManager.d.ts.map +1 -1
  65. package/dist/device/Device.d.ts +5 -3
  66. package/dist/device/Device.d.ts.map +1 -1
  67. package/dist/device/DeviceCommands.d.ts +12 -12
  68. package/dist/device/DeviceCommands.d.ts.map +1 -1
  69. package/dist/device/DeviceConnector.d.ts +2 -1
  70. package/dist/device/DeviceConnector.d.ts.map +1 -1
  71. package/dist/events/ui-request.d.ts +8 -0
  72. package/dist/events/ui-request.d.ts.map +1 -1
  73. package/dist/index.d.ts +207 -23
  74. package/dist/index.js +2815 -182
  75. package/dist/inject.d.ts.map +1 -1
  76. package/dist/protocols/protocol-v2/features.d.ts +56 -0
  77. package/dist/protocols/protocol-v2/features.d.ts.map +1 -0
  78. package/dist/protocols/protocol-v2/firmware.d.ts +12 -0
  79. package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -0
  80. package/dist/protocols/protocol-v2/index.d.ts +3 -0
  81. package/dist/protocols/protocol-v2/index.d.ts.map +1 -0
  82. package/dist/types/api/export.d.ts +2 -1
  83. package/dist/types/api/export.d.ts.map +1 -1
  84. package/dist/types/api/firmwareUpdate.d.ts +7 -0
  85. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  86. package/dist/types/api/index.d.ts +30 -1
  87. package/dist/types/api/index.d.ts.map +1 -1
  88. package/dist/types/api/protocolV2.d.ts +123 -0
  89. package/dist/types/api/protocolV2.d.ts.map +1 -0
  90. package/dist/types/api/searchDevices.d.ts +2 -2
  91. package/dist/types/api/searchDevices.d.ts.map +1 -1
  92. package/dist/types/api/tonSignData.d.ts +18 -0
  93. package/dist/types/api/tonSignData.d.ts.map +1 -0
  94. package/dist/types/device.d.ts +1 -1
  95. package/dist/types/device.d.ts.map +1 -1
  96. package/dist/types/params.d.ts +2 -0
  97. package/dist/types/params.d.ts.map +1 -1
  98. package/dist/types/settings.d.ts +1 -1
  99. package/dist/types/settings.d.ts.map +1 -1
  100. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  101. package/dist/utils/patch.d.ts +1 -1
  102. package/dist/utils/patch.d.ts.map +1 -1
  103. package/dist/utils/versionUtils.d.ts +1 -1
  104. package/package.json +5 -5
  105. package/src/api/DirList.ts +25 -0
  106. package/src/api/DirMake.ts +20 -0
  107. package/src/api/DirRemove.ts +20 -0
  108. package/src/api/FileDelete.ts +20 -0
  109. package/src/api/FileRead.ts +161 -0
  110. package/src/api/FileWrite.ts +191 -0
  111. package/src/api/FirmwareUpdateV3.ts +21 -4
  112. package/src/api/FirmwareUpdateV4.ts +810 -0
  113. package/src/api/GetOnekeyFeatures.ts +75 -3
  114. package/src/api/PathInfo.ts +24 -0
  115. package/src/api/SearchDevices.ts +7 -2
  116. package/src/api/alephium/AlephiumSignTransaction.ts +6 -5
  117. package/src/api/device/DeviceRebootToBoardloader.ts +10 -1
  118. package/src/api/device/DeviceRebootToBootloader.ts +10 -1
  119. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +27 -4
  120. package/src/api/index.ts +29 -0
  121. package/src/api/protocol-v2/DevFirmwareUpdate.ts +33 -0
  122. package/src/api/protocol-v2/DevGetDeviceInfo.ts +35 -0
  123. package/src/api/protocol-v2/DevGetFirmwareUpdateStatus.ts +18 -0
  124. package/src/api/protocol-v2/DevGetOnboardingStatus.ts +18 -0
  125. package/src/api/protocol-v2/DevReboot.ts +22 -0
  126. package/src/api/protocol-v2/FactoryDeviceInfoSettings.ts +27 -0
  127. package/src/api/protocol-v2/FactoryGetDeviceInfo.ts +18 -0
  128. package/src/api/protocol-v2/FilesystemFixPermission.ts +14 -0
  129. package/src/api/protocol-v2/FilesystemFormat.ts +14 -0
  130. package/src/api/protocol-v2/GetProtoVersion.ts +14 -0
  131. package/src/api/protocol-v2/Ping.ts +16 -0
  132. package/src/api/protocol-v2/helpers.ts +140 -0
  133. package/src/api/ton/TonSignData.ts +60 -0
  134. package/src/constants/index.ts +1 -1
  135. package/src/core/index.ts +19 -2
  136. package/src/data/messages/messages-pro2.json +733 -0
  137. package/src/data/messages/messages.json +98 -0
  138. package/src/data-manager/DataManager.ts +6 -2
  139. package/src/data-manager/TransportManager.ts +29 -3
  140. package/src/device/Device.ts +66 -8
  141. package/src/device/DeviceCommands.ts +160 -26
  142. package/src/device/DeviceConnector.ts +29 -4
  143. package/src/device/DevicePool.ts +1 -1
  144. package/src/events/ui-request.ts +8 -0
  145. package/src/inject.ts +45 -1
  146. package/src/protocols/protocol-v2/features.ts +259 -0
  147. package/src/protocols/protocol-v2/firmware.ts +26 -0
  148. package/src/protocols/protocol-v2/index.ts +2 -0
  149. package/src/types/api/export.ts +2 -0
  150. package/src/types/api/firmwareUpdate.ts +12 -0
  151. package/src/types/api/index.ts +65 -1
  152. package/src/types/api/protocolV2.ts +221 -0
  153. package/src/types/api/searchDevices.ts +2 -2
  154. package/src/types/api/tonSignData.ts +29 -0
  155. package/src/types/device.ts +3 -1
  156. package/src/types/params.ts +7 -0
  157. package/src/types/settings.ts +1 -1
  158. package/src/utils/deviceInfoUtils.ts +9 -5
@@ -11569,6 +11569,102 @@
11569
11569
  }
11570
11570
  }
11571
11571
  },
11572
+ "TonSignData": {
11573
+ "fields": {
11574
+ "address_n": {
11575
+ "rule": "repeated",
11576
+ "type": "uint32",
11577
+ "id": 1,
11578
+ "options": {
11579
+ "packed": false
11580
+ }
11581
+ },
11582
+ "type": {
11583
+ "rule": "required",
11584
+ "type": "TonSignDataType",
11585
+ "id": 2
11586
+ },
11587
+ "payload": {
11588
+ "rule": "required",
11589
+ "type": "bytes",
11590
+ "id": 3
11591
+ },
11592
+ "schema": {
11593
+ "type": "string",
11594
+ "id": 4
11595
+ },
11596
+ "appdomain": {
11597
+ "rule": "required",
11598
+ "type": "string",
11599
+ "id": 5
11600
+ },
11601
+ "timestamp": {
11602
+ "rule": "required",
11603
+ "type": "uint64",
11604
+ "id": 6
11605
+ },
11606
+ "from_address": {
11607
+ "type": "string",
11608
+ "id": 7
11609
+ },
11610
+ "wallet_version": {
11611
+ "type": "TonWalletVersion",
11612
+ "id": 8,
11613
+ "options": {
11614
+ "default": "V4R2"
11615
+ }
11616
+ },
11617
+ "wallet_id": {
11618
+ "type": "uint32",
11619
+ "id": 9,
11620
+ "options": {
11621
+ "default": 698983191
11622
+ }
11623
+ },
11624
+ "workchain": {
11625
+ "type": "TonWorkChain",
11626
+ "id": 10,
11627
+ "options": {
11628
+ "default": "BASECHAIN"
11629
+ }
11630
+ },
11631
+ "is_bounceable": {
11632
+ "type": "bool",
11633
+ "id": 11,
11634
+ "options": {
11635
+ "default": false
11636
+ }
11637
+ },
11638
+ "is_testnet_only": {
11639
+ "type": "bool",
11640
+ "id": 12,
11641
+ "options": {
11642
+ "default": false
11643
+ }
11644
+ }
11645
+ },
11646
+ "nested": {
11647
+ "TonSignDataType": {
11648
+ "values": {
11649
+ "TEXT": 0,
11650
+ "BINARY": 1,
11651
+ "CELL": 2
11652
+ }
11653
+ }
11654
+ }
11655
+ },
11656
+ "TonSignedData": {
11657
+ "fields": {
11658
+ "signature": {
11659
+ "type": "bytes",
11660
+ "id": 1
11661
+ },
11662
+ "digest": {
11663
+ "type": "bytes",
11664
+ "id": 2
11665
+ }
11666
+ }
11667
+ },
11572
11668
  "TronGetAddress": {
11573
11669
  "fields": {
11574
11670
  "address_n": {
@@ -12501,6 +12597,8 @@
12501
12597
  "MessageType_TonSignProof": 11905,
12502
12598
  "MessageType_TonSignedProof": 11906,
12503
12599
  "MessageType_TonTxAck": 11907,
12600
+ "MessageType_TonSignData": 11908,
12601
+ "MessageType_TonSignedData": 11909,
12504
12602
  "MessageType_ScdoGetAddress": 12001,
12505
12603
  "MessageType_ScdoAddress": 12002,
12506
12604
  "MessageType_ScdoSignTx": 12003,
@@ -4,6 +4,7 @@ import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
4
4
 
5
5
  import MessagesJSON from '../data/messages/messages.json';
6
6
  import MessagesLegacyV1JSON from '../data/messages/messages_legacy_v1.json';
7
+ import MessagesPro2JSON from '../data/messages/messages-pro2.json';
7
8
  import {
8
9
  LoggerNames,
9
10
  getDeviceBLEFirmwareVersion,
@@ -40,7 +41,7 @@ export const FIRMWARE_FIELDS = [
40
41
 
41
42
  export type IFirmwareField = (typeof FIRMWARE_FIELDS)[number];
42
43
 
43
- export type MessageVersion = 'latest' | 'v1';
44
+ export type MessageVersion = 'latest' | 'v1' | 'protocolV2';
44
45
 
45
46
  const FIRMWARE_FIELD_TYPE_MAP: Readonly<Record<IFirmwareField, EFirmwareType>> = {
46
47
  firmware: EFirmwareType.Universal,
@@ -62,7 +63,9 @@ function getFirmwareTypeFromField(firmwareField: IFirmwareField): EFirmwareType
62
63
  }
63
64
 
64
65
  export default class DataManager {
65
- static deviceMap: DeviceTypeMap = {
66
+ static deviceMap: DeviceTypeMap & {
67
+ [k: string]: DeviceTypeMap[keyof DeviceTypeMap] | undefined;
68
+ } = {
66
69
  [EDeviceType.Classic]: {
67
70
  firmware: [],
68
71
  ble: [],
@@ -96,6 +99,7 @@ export default class DataManager {
96
99
  static messages: { [version in MessageVersion]: JSON } = {
97
100
  latest: MessagesJSON as unknown as JSON,
98
101
  v1: MessagesLegacyV1JSON as unknown as JSON,
102
+ protocolV2: MessagesPro2JSON as unknown as JSON,
99
103
  };
100
104
 
101
105
  static lastCheckTimestamp = 0;
@@ -17,6 +17,7 @@ const LowLevelLogger = getLogger(LoggerNames.HdTransportLowLevel);
17
17
  const NodeUsbLogger = getLogger(LoggerNames.HdTransportNodeUsb);
18
18
  const WebBleLogger = getLogger(LoggerNames.HdWebBleTransport);
19
19
  const WebUsbLogger = getLogger(LoggerNames.HdTransportWebUsb);
20
+ const REACT_NATIVE_BLE_SCAN_TIMEOUT_MS = 8000;
20
21
 
21
22
  /**
22
23
  * transport 在同一个环境中只会存在一个
@@ -74,6 +75,9 @@ export default class TransportManager {
74
75
  }
75
76
  Log.debug('Configuring transports');
76
77
  await this.transport.configure(JSON.stringify(this.defaultMessages));
78
+ this.currentMessages = this.defaultMessages;
79
+ this.messageVersion = 'latest';
80
+ await this.configureProtocolV2Messages();
77
81
  Log.debug('Configuring transports done');
78
82
  } catch (error) {
79
83
  Log.debug('Initializing transports error: ', error);
@@ -83,8 +87,19 @@ export default class TransportManager {
83
87
  }
84
88
  }
85
89
 
86
- static async reconfigure(features?: Features | undefined) {
87
- Log.debug(`Begin reconfiguring transports`);
90
+ /**
91
+ * Re-load the transport's main protobuf schema based on a device's reported features.
92
+ *
93
+ * This handles message-version compatibility within Protocol V1 (e.g. Touch's classic
94
+ * vs latest schema). It is NOT used to switch between Protocol V1 and Protocol V2 —
95
+ * the transport already holds both schemas after initial configure(), and routes per
96
+ * device by `getProtocolType()`.
97
+ */
98
+ static async reconfigure(features?: Features) {
99
+ if (!features) {
100
+ return;
101
+ }
102
+
88
103
  const { messageVersion, messages } = getSupportMessageVersion(features);
89
104
 
90
105
  if (this.currentMessages === messages || !messages) {
@@ -109,7 +124,9 @@ export default class TransportManager {
109
124
  const env = DataManager.getSettings('env');
110
125
  if (env === 'react-native') {
111
126
  /** Actually initializes the ReactNativeTransport */
112
- this.transport = new TransportConstructor({ scanTimeout: 3000 }) as unknown as Transport;
127
+ this.transport = new TransportConstructor({
128
+ scanTimeout: REACT_NATIVE_BLE_SCAN_TIMEOUT_MS,
129
+ }) as unknown as Transport;
113
130
  } else {
114
131
  /** Actually initializes the HttpTransport */
115
132
  this.transport = new TransportConstructor() as unknown as Transport;
@@ -130,6 +147,15 @@ export default class TransportManager {
130
147
  return this.transport;
131
148
  }
132
149
 
150
+ private static async configureProtocolV2Messages() {
151
+ const protocolV2Messages = DataManager.getProtobufMessages('protocolV2');
152
+ const { configureProtocolV2 } = this.transport;
153
+ if (protocolV2Messages && typeof configureProtocolV2 === 'function') {
154
+ await configureProtocolV2.call(this.transport, JSON.stringify(protocolV2Messages));
155
+ Log.debug('Protocol V2 messages configured');
156
+ }
157
+ }
158
+
133
159
  static getDefaultMessages() {
134
160
  return this.defaultMessages;
135
161
  }
@@ -41,6 +41,7 @@ import { DataManager } from '../data-manager';
41
41
  import TransportManager from '../data-manager/TransportManager';
42
42
  import { toHardened } from '../api/helpers/pathUtils';
43
43
  import { existCapability } from '../utils/capabilitieUtils';
44
+ import { getProtocolV2Features } from '../protocols/protocol-v2';
44
45
 
45
46
  import type { PROTO } from '../constants';
46
47
  import type {
@@ -49,7 +50,7 @@ import type {
49
50
  PassphraseRequestPayload,
50
51
  } from '../events';
51
52
  import type { PassphrasePromptResponse } from './DeviceCommands';
52
- import type { Deferred } from '@onekeyfe/hd-shared';
53
+ import type { Deferred, HardwareConnectProtocol } from '@onekeyfe/hd-shared';
53
54
  import type { OneKeyDeviceInfo as DeviceDescriptor } from '@onekeyfe/hd-transport';
54
55
  import type DeviceConnector from './DeviceConnector';
55
56
 
@@ -58,6 +59,7 @@ export type InitOptions = {
58
59
  deviceId?: string;
59
60
  passphraseState?: string;
60
61
  deriveCardano?: boolean;
62
+ connectProtocol?: HardwareConnectProtocol;
61
63
  };
62
64
 
63
65
  export type RunOptions = {
@@ -252,13 +254,13 @@ export class Device extends EventEmitter {
252
254
  * Device connect
253
255
  * @returns {Promise<boolean>}
254
256
  */
255
- connect() {
257
+ connect(connectProtocol?: HardwareConnectProtocol) {
256
258
  const env = DataManager.getSettings('env');
257
259
  // eslint-disable-next-line no-async-promise-executor
258
260
  return new Promise<boolean>(async (resolve, reject) => {
259
261
  if (DataManager.isBleConnect(env)) {
260
262
  try {
261
- await this.acquire();
263
+ await this.acquire(connectProtocol);
262
264
  resolve(true);
263
265
  } catch (error) {
264
266
  reject(error);
@@ -268,7 +270,7 @@ export class Device extends EventEmitter {
268
270
  // 不存在 Session ID 或存在 Session ID 但设备在别处使用,都需要 acquire 获取最新 sessionID
269
271
  if (!this.mainId || (!this.isUsedHere() && this.originalDescriptor)) {
270
272
  try {
271
- await this.acquire();
273
+ await this.acquire(connectProtocol);
272
274
  resolve(true);
273
275
  } catch (error) {
274
276
  reject(error);
@@ -283,23 +285,40 @@ export class Device extends EventEmitter {
283
285
  });
284
286
  }
285
287
 
286
- async acquire() {
288
+ async acquire(connectProtocol?: HardwareConnectProtocol) {
287
289
  const env = DataManager.getSettings('env');
288
290
  const mainIdKey = DataManager.isBleConnect(env) ? 'id' : 'session';
291
+ const expectedProtocol = connectProtocol ?? this.originalDescriptor.protocolType;
289
292
  try {
290
293
  if (DataManager.isBleConnect(env)) {
291
- const res = await this.deviceConnector?.acquire(this.originalDescriptor.id);
294
+ const res = await this.deviceConnector?.acquire(
295
+ this.originalDescriptor.id,
296
+ undefined,
297
+ true,
298
+ expectedProtocol
299
+ );
292
300
  this.mainId = (res as unknown as any).uuid ?? '';
293
301
  Log.debug('Expected uuid:', this.mainId);
294
302
  } else {
295
303
  this.mainId = await this.deviceConnector?.acquire(
296
304
  this.originalDescriptor.path,
297
- this.originalDescriptor.session
305
+ this.originalDescriptor.session,
306
+ undefined,
307
+ expectedProtocol
298
308
  );
299
309
  Log.debug('Expected session id:', this.mainId);
300
310
  }
301
311
  this.deviceAcquired = true;
302
312
  this.updateDescriptor({ [mainIdKey]: this.mainId } as unknown as DeviceDescriptor);
313
+
314
+ // Propagate protocol version detected during acquire.
315
+ const detectedProtocol = TransportManager.transport?.getProtocolType?.(
316
+ DataManager.isBleConnect(env) ? this.originalDescriptor.id : this.originalDescriptor.path
317
+ );
318
+ if (detectedProtocol) {
319
+ this.originalDescriptor.protocolType = detectedProtocol;
320
+ }
321
+
303
322
  if (this.commands) {
304
323
  await this.commands.dispose(false);
305
324
  }
@@ -462,6 +481,12 @@ export class Device extends EventEmitter {
462
481
  }
463
482
 
464
483
  async initialize(options?: InitOptions) {
484
+ // Protocol V2 不支持传统 Initialize,直接使用协议专用初始化流程。
485
+ if (this.originalDescriptor.protocolType === 'V2') {
486
+ await this._initializeProtocolV2();
487
+ return;
488
+ }
489
+
465
490
  // Log.debug('initialize param:', options);
466
491
 
467
492
  this.passphraseState = options?.passphraseState;
@@ -510,6 +535,39 @@ export class Device extends EventEmitter {
510
535
  }
511
536
  }
512
537
 
538
+ /**
539
+ * Device initialization over Protocol V2.
540
+ *
541
+ * Protocol V2 不走传统 Initialize/GetFeatures。这里通过
542
+ * Ping + DevGetDeviceInfo 建立统一的 Features 视图。
543
+ */
544
+ private async _initializeProtocolV2() {
545
+ Log.debug('Initialize device via Protocol V2 feature adapter');
546
+
547
+ try {
548
+ const features = await Promise.race([
549
+ getProtocolV2Features({
550
+ commands: this.commands,
551
+ descriptor: this.originalDescriptor,
552
+ onDeviceInfoError: error => {
553
+ Log.debug('Protocol V2 DevGetDeviceInfo failed:', error);
554
+ },
555
+ timeoutMs: 10 * 1000,
556
+ }),
557
+ new Promise<never>((_, reject) => {
558
+ setTimeout(() => {
559
+ reject(ERRORS.TypedError(HardwareErrorCode.DeviceInitializeFailed));
560
+ }, 10 * 1000);
561
+ }),
562
+ ]);
563
+ Log.debug('Protocol V2 normalized features:', features);
564
+ this._updateFeatures(features);
565
+ } catch (error) {
566
+ Log.error('Protocol V2 initialization failed:', error);
567
+ throw error;
568
+ }
569
+ }
570
+
513
571
  async getFeatures() {
514
572
  const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
515
573
  this._updateFeatures(message);
@@ -580,7 +638,7 @@ export class Device extends EventEmitter {
580
638
  const env = DataManager.getSettings('env');
581
639
  if (env !== 'react-native') {
582
640
  try {
583
- await this.acquire();
641
+ await this.acquire(options.connectProtocol);
584
642
  } catch (error) {
585
643
  this.runPromise = null;
586
644
  return Promise.reject(error);
@@ -12,7 +12,12 @@ import {
12
12
  } from '../utils/tracing';
13
13
 
14
14
  import type { Device } from './Device';
15
- import type { FailureType, Messages, Transport } from '@onekeyfe/hd-transport';
15
+ import type {
16
+ FailureType,
17
+ Messages,
18
+ Transport,
19
+ TransportCallOptions,
20
+ } from '@onekeyfe/hd-transport';
16
21
 
17
22
  export type PassphrasePromptResponse = {
18
23
  passphrase?: string;
@@ -22,16 +27,116 @@ export type PassphrasePromptResponse = {
22
27
  };
23
28
 
24
29
  type MessageType = Messages.MessageType;
25
- type MessageKey = keyof MessageType;
30
+ type MessageKey = Extract<keyof MessageType, string>;
26
31
  export type TypedResponseMessage<T extends MessageKey> = {
27
32
  type: T;
28
33
  message: MessageType[T];
29
34
  };
30
35
  type TypedCallResponseMap = {
31
- [K in keyof MessageType]: TypedResponseMessage<K>;
36
+ [K in MessageKey]: TypedResponseMessage<K>;
32
37
  };
33
38
  export type DefaultMessageResponse = TypedCallResponseMap[keyof MessageType];
34
39
 
40
+ const MAX_DEBUG_ARRAY_ITEMS = 20;
41
+ const MAX_DEBUG_OBJECT_KEYS = 40;
42
+ const MAX_DEBUG_STRING_LENGTH = 512;
43
+ const MAX_DEBUG_DEPTH = 4;
44
+ const HIGH_VOLUME_DEBUG_CALLS = new Set([
45
+ 'FilesystemFileWrite',
46
+ 'FileWrite',
47
+ 'EmmcFileWrite',
48
+ 'FirmwareUpload',
49
+ 'ResourceAck',
50
+ ]);
51
+
52
+ function shouldReduceDebugForCall(type: string) {
53
+ return HIGH_VOLUME_DEBUG_CALLS.has(type);
54
+ }
55
+
56
+ function getBinaryByteLength(value: unknown): number | undefined {
57
+ if (value instanceof ArrayBuffer) {
58
+ return value.byteLength;
59
+ }
60
+
61
+ if (ArrayBuffer.isView(value)) {
62
+ return value.byteLength;
63
+ }
64
+
65
+ if (typeof Blob !== 'undefined' && value instanceof Blob) {
66
+ return value.size;
67
+ }
68
+
69
+ return undefined;
70
+ }
71
+
72
+ function summarizeRedactedData(value: unknown): string {
73
+ const byteLength = getBinaryByteLength(value);
74
+ if (byteLength !== undefined) {
75
+ return `[redacted data: ${byteLength} bytes]`;
76
+ }
77
+
78
+ if (typeof value === 'string') {
79
+ return `[redacted data: string length=${value.length}]`;
80
+ }
81
+
82
+ if (Array.isArray(value)) {
83
+ return `[redacted data: array length=${value.length}]`;
84
+ }
85
+
86
+ if (value && typeof value === 'object') {
87
+ return `[redacted data: object keys=${Object.keys(value).length}]`;
88
+ }
89
+
90
+ return `[redacted data: ${typeof value}]`;
91
+ }
92
+
93
+ function sanitizeDebugPayload(value: unknown, key = '', depth = 0): unknown {
94
+ if (key === 'data' && value !== null && value !== undefined) {
95
+ return summarizeRedactedData(value);
96
+ }
97
+
98
+ const byteLength = getBinaryByteLength(value);
99
+ if (byteLength !== undefined) {
100
+ return `[binary: ${byteLength} bytes]`;
101
+ }
102
+
103
+ if (typeof value === 'string') {
104
+ return value.length > MAX_DEBUG_STRING_LENGTH
105
+ ? `${value.slice(0, MAX_DEBUG_STRING_LENGTH)}... (len=${value.length})`
106
+ : value;
107
+ }
108
+
109
+ if (!value || typeof value !== 'object') {
110
+ return value;
111
+ }
112
+
113
+ if (depth >= MAX_DEBUG_DEPTH) {
114
+ return Array.isArray(value)
115
+ ? `[array length=${value.length}]`
116
+ : `[object keys=${Object.keys(value).length}]`;
117
+ }
118
+
119
+ if (Array.isArray(value)) {
120
+ const items = value
121
+ .slice(0, MAX_DEBUG_ARRAY_ITEMS)
122
+ .map(item => sanitizeDebugPayload(item, key, depth + 1));
123
+ if (value.length > MAX_DEBUG_ARRAY_ITEMS) {
124
+ items.push(`... (${value.length - MAX_DEBUG_ARRAY_ITEMS} more)`);
125
+ }
126
+ return items;
127
+ }
128
+
129
+ const entries = Object.entries(value).slice(0, MAX_DEBUG_OBJECT_KEYS);
130
+ const sanitized: Record<string, unknown> = {};
131
+ entries.forEach(([entryKey, entryValue]) => {
132
+ sanitized[entryKey] = sanitizeDebugPayload(entryValue, entryKey, depth + 1);
133
+ });
134
+ if (Object.keys(value).length > MAX_DEBUG_OBJECT_KEYS) {
135
+ sanitized.__truncated__ = `${Object.keys(value).length - MAX_DEBUG_OBJECT_KEYS} more keys`;
136
+ }
137
+ return sanitized;
138
+ }
139
+
35
140
  const assertType = (res: DefaultMessageResponse, resType: string | string[]) => {
36
141
  const splitResTypes = Array.isArray(resType) ? resType : resType.split('|');
37
142
  if (!splitResTypes.includes(res.type)) {
@@ -224,17 +329,21 @@ export class DeviceCommands {
224
329
  // Sends an async message to the opened device.
225
330
  async call(
226
331
  type: MessageKey,
227
- msg: DefaultMessageResponse['message'] = {}
332
+ msg?: DefaultMessageResponse['message'],
333
+ options?: TransportCallOptions
228
334
  ): Promise<DefaultMessageResponse> {
229
- Log.debug('[DeviceCommands] [call] Sending', type);
335
+ const shouldReduceDebug = shouldReduceDebugForCall(type);
336
+ if (!shouldReduceDebug) {
337
+ Log.debug('[DeviceCommands] [call] Sending', type);
338
+ }
230
339
 
231
340
  try {
232
- const promise = this.transport.call(this.mainId, type, msg) as any;
341
+ const promise = this.transport.call(this.mainId, type, msg ?? {}, options) as any;
233
342
  this.callPromise = promise;
234
343
  const res = await promise;
235
344
  if (res.type === 'Failure') {
236
345
  LogCore.debug('[DeviceCommands] [call] Received', res.type, res.message);
237
- } else {
346
+ } else if (!shouldReduceDebug) {
238
347
  LogCore.debug('[DeviceCommands] [call] Received', res.type);
239
348
  }
240
349
  return res;
@@ -283,19 +392,22 @@ export class DeviceCommands {
283
392
  typedCall<T extends MessageKey, R extends MessageKey[]>(
284
393
  type: T,
285
394
  resType: R,
286
- msg?: MessageType[T]
395
+ msg?: MessageType[T],
396
+ options?: TransportCallOptions
287
397
  ): Promise<TypedCallResponseMap[R[number]]>;
288
398
 
289
399
  typedCall<T extends MessageKey, R extends MessageKey>(
290
400
  type: T,
291
401
  resType: R,
292
- msg?: MessageType[T]
402
+ msg?: MessageType[T],
403
+ options?: TransportCallOptions
293
404
  ): Promise<TypedResponseMessage<R>>;
294
405
 
295
406
  async typedCall(
296
407
  type: MessageKey,
297
408
  resType: MessageKey | MessageKey[],
298
- msg?: DefaultMessageResponse['message']
409
+ msg?: DefaultMessageResponse['message'],
410
+ options?: TransportCallOptions
299
411
  ) {
300
412
  if (this.disposed) {
301
413
  throw ERRORS.TypedError(
@@ -312,16 +424,21 @@ export class DeviceCommands {
312
424
  'PassphraseAck',
313
425
  'Cancel',
314
426
  'BixinPinInputOnDevice',
427
+ 'FilesystemFileWrite',
428
+ 'FileWrite',
429
+ 'EmmcFileWrite',
430
+ 'FirmwareUpload',
431
+ 'ResourceAck',
315
432
  ] as any;
316
433
  if (!skipTypes.includes(type) && msg) {
317
434
  // Use debug channel to avoid noise escalation
318
- Log.debug('[DeviceCommands] [typedCall] Sending payload', type, msg);
435
+ Log.debug('[DeviceCommands] [typedCall] Sending payload', type, sanitizeDebugPayload(msg));
319
436
  }
320
437
  } catch (e) {
321
438
  // ignore logging errors
322
439
  }
323
440
 
324
- const response = await this._commonCall(type, msg);
441
+ const response = await this._commonCall(type, msg, options);
325
442
  try {
326
443
  assertType(response, resType);
327
444
  } catch (error) {
@@ -334,6 +451,12 @@ export class DeviceCommands {
334
451
  // throw bridge network error
335
452
  if (error instanceof HardwareError) {
336
453
  if (error.errorCode === HardwareErrorCode.ResponseUnexpectTypeError) {
454
+ Log.debug('[DeviceCommands] [typedCall] Unexpected response type', {
455
+ request: type,
456
+ expected: resType,
457
+ received: response.type,
458
+ response: sanitizeDebugPayload(response.message),
459
+ });
337
460
  // Do not intercept CallMethodError
338
461
  // Do not intercept “assertType: Response of unexpected type” error
339
462
  // Blocking the above two messages will not know what the specific error message is, and the specific error should be handled by the subsequent business logic.
@@ -347,7 +470,7 @@ export class DeviceCommands {
347
470
  if (error.message.indexOf('BridgeDeviceDisconnected') > -1) {
348
471
  throw ERRORS.TypedError(HardwareErrorCode.BridgeDeviceDisconnected);
349
472
  }
350
- throw ERRORS.TypedError(HardwareErrorCode.ResponseUnexpectTypeError);
473
+ throw error;
351
474
  }
352
475
  } else {
353
476
  // throw error anyway, next call should be resolved properly// throw error anyway, next call should be resolved properly
@@ -357,20 +480,27 @@ export class DeviceCommands {
357
480
  return response;
358
481
  }
359
482
 
360
- async _commonCall(type: MessageKey, msg?: DefaultMessageResponse['message']) {
361
- const resp = await this.call(type, msg);
362
- return this._filterCommonTypes(resp, type);
483
+ async _commonCall(
484
+ type: MessageKey,
485
+ msg?: DefaultMessageResponse['message'],
486
+ options?: TransportCallOptions
487
+ ) {
488
+ const resp = await this.call(type, msg, options);
489
+ return this._filterCommonTypes(resp, type, options);
363
490
  }
364
491
 
365
492
  _filterCommonTypes(
366
493
  res: DefaultMessageResponse,
367
- callType: MessageKey
494
+ callType: MessageKey,
495
+ options?: TransportCallOptions
368
496
  ): Promise<DefaultMessageResponse> {
369
497
  try {
370
- if (DataManager.getSettings('env') === 'react-native') {
371
- Log.debug('_filterCommonTypes: ', JSON.stringify(res));
498
+ if (shouldReduceDebugForCall(callType)) {
499
+ // 高频文件写入每个 chunk 都会经过这里,避免 debug log 反向拖慢传输。
500
+ } else if (DataManager.getSettings('env') === 'react-native') {
501
+ Log.debug('_filterCommonTypes: ', JSON.stringify(sanitizeDebugPayload(res)));
372
502
  } else {
373
- Log.debug('_filterCommonTypes: ', res);
503
+ Log.debug('_filterCommonTypes: ', sanitizeDebugPayload(res));
374
504
  }
375
505
  } catch (error) {
376
506
  // ignore
@@ -400,6 +530,10 @@ export class DeviceCommands {
400
530
  error = ERRORS.TypedError(HardwareErrorCode.PinCancelled);
401
531
  }
402
532
 
533
+ if (code === 'Failure_PinMismatch') {
534
+ error = ERRORS.TypedError(HardwareErrorCode.PinMismatch, message);
535
+ }
536
+
403
537
  if (code === 'Failure_DataError') {
404
538
  if (message === 'Please confirm the BlindSign enabled') {
405
539
  error = ERRORS.TypedError(HardwareErrorCode.BlindSignDisabled);
@@ -468,7 +602,7 @@ export class DeviceCommands {
468
602
  } else {
469
603
  this.device.emit(DEVICE.BUTTON, this.device, res.message);
470
604
  }
471
- return this._commonCall('ButtonAck', {});
605
+ return this._commonCall('ButtonAck', {}, options);
472
606
  }
473
607
 
474
608
  if (res.type === 'EntropyRequest') {
@@ -481,11 +615,11 @@ export class DeviceCommands {
481
615
  if (pin === '@@ONEKEY_INPUT_PIN_IN_DEVICE') {
482
616
  // only classic\1s\mini\pure
483
617
  this.device.setCancelableAction(() => this.cancelDeviceOnOneKeyDevice());
484
- return this._commonCall('BixinPinInputOnDevice').finally(() => {
618
+ return this._commonCall('BixinPinInputOnDevice', {}, options).finally(() => {
485
619
  this.device.clearCancelableAction();
486
620
  });
487
621
  }
488
- return this._commonCall('PinMatrixAck', { pin });
622
+ return this._commonCall('PinMatrixAck', { pin }, options);
489
623
  },
490
624
  error => Promise.reject(error)
491
625
  );
@@ -500,12 +634,12 @@ export class DeviceCommands {
500
634
 
501
635
  // Attach PIN on device
502
636
  if (attachPinOnDevice && existsAttachPinUser) {
503
- return this._commonCall('PassphraseAck', { on_device_attach_pin: true });
637
+ return this._commonCall('PassphraseAck', { on_device_attach_pin: true }, options);
504
638
  }
505
639
 
506
640
  return !passphraseOnDevice
507
- ? this._commonCall('PassphraseAck', { passphrase })
508
- : this._commonCall('PassphraseAck', { on_device: true });
641
+ ? this._commonCall('PassphraseAck', { passphrase }, options)
642
+ : this._commonCall('PassphraseAck', { on_device: true }, options);
509
643
  });
510
644
  }
511
645