@onekeyfe/hd-core 1.2.8-alpha.13 → 1.2.8-alpha.130

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 (125) hide show
  1. package/__tests__/allNetworkGetAddress-privacy-networks.test.ts +78 -0
  2. package/__tests__/core-dispose.test.ts +114 -1
  3. package/__tests__/core-error-output.test.ts +21 -0
  4. package/__tests__/device-connector-protocol.test.ts +5 -2
  5. package/__tests__/device-lifecycle-events.test.ts +118 -3
  6. package/__tests__/firmware-update/firmware-artifact-source.test.ts +11 -0
  7. package/__tests__/logBlockEvent.test.ts +292 -0
  8. package/__tests__/logger.test.ts +18 -0
  9. package/__tests__/monero-get-address.test.ts +147 -0
  10. package/__tests__/monero-key-image-sync.test.ts +272 -0
  11. package/__tests__/monero-sign-transaction.test.ts +436 -0
  12. package/__tests__/protocol-v2-resources.test.ts +14 -0
  13. package/__tests__/protocol-v2.test.ts +845 -3
  14. package/__tests__/protocolV2FileWrite.test.ts +434 -7
  15. package/__tests__/search-devices.test.ts +36 -0
  16. package/__tests__/usb-identity-probe.test.ts +358 -0
  17. package/__tests__/zcash-protobuf.test.ts +141 -0
  18. package/__tests__/zcash-sign-pczt.test.ts +446 -0
  19. package/dist/api/BaseMethod.d.ts.map +1 -1
  20. package/dist/api/FileWrite.d.ts.map +1 -1
  21. package/dist/api/FirmwareUpdateV4.d.ts +5 -0
  22. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  23. package/dist/api/SearchDevices.d.ts +21 -1
  24. package/dist/api/SearchDevices.d.ts.map +1 -1
  25. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  26. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -1
  27. package/dist/api/helpers/pathUtils.d.ts +1 -0
  28. package/dist/api/helpers/pathUtils.d.ts.map +1 -1
  29. package/dist/api/helpers/protocolV2FileWrite.d.ts +5 -1
  30. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  31. package/dist/api/index.d.ts +6 -0
  32. package/dist/api/index.d.ts.map +1 -1
  33. package/dist/api/monero/MoneroGetAddress.d.ts +11 -0
  34. package/dist/api/monero/MoneroGetAddress.d.ts.map +1 -0
  35. package/dist/api/monero/MoneroGetWatchKey.d.ts +11 -0
  36. package/dist/api/monero/MoneroGetWatchKey.d.ts.map +1 -0
  37. package/dist/api/monero/MoneroKeyImageSync.d.ts +20 -0
  38. package/dist/api/monero/MoneroKeyImageSync.d.ts.map +1 -0
  39. package/dist/api/monero/MoneroSignTransaction.d.ts +17 -0
  40. package/dist/api/monero/MoneroSignTransaction.d.ts.map +1 -0
  41. package/dist/api/monero/helpers.d.ts +5 -0
  42. package/dist/api/monero/helpers.d.ts.map +1 -0
  43. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
  44. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  45. package/dist/api/zcash/ZcashGetAddress.d.ts +12 -0
  46. package/dist/api/zcash/ZcashGetAddress.d.ts.map +1 -0
  47. package/dist/api/zcash/ZcashSignPczt.d.ts +16 -0
  48. package/dist/api/zcash/ZcashSignPczt.d.ts.map +1 -0
  49. package/dist/core/index.d.ts +3 -1
  50. package/dist/core/index.d.ts.map +1 -1
  51. package/dist/device/Device.d.ts +1 -0
  52. package/dist/device/Device.d.ts.map +1 -1
  53. package/dist/device/DeviceCommands.d.ts +4 -4
  54. package/dist/events/logBlockEvent.d.ts +10 -0
  55. package/dist/events/logBlockEvent.d.ts.map +1 -1
  56. package/dist/events/ui-request.d.ts +2 -0
  57. package/dist/events/ui-request.d.ts.map +1 -1
  58. package/dist/index.d.ts +183 -7
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +5625 -4192
  61. package/dist/inject.d.ts.map +1 -1
  62. package/dist/protocols/protocol-v2/resources.d.ts +1 -0
  63. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  64. package/dist/types/api/allNetworkGetAddress.d.ts +5 -1
  65. package/dist/types/api/allNetworkGetAddress.d.ts.map +1 -1
  66. package/dist/types/api/export.d.ts +6 -0
  67. package/dist/types/api/export.d.ts.map +1 -1
  68. package/dist/types/api/index.d.ts +12 -0
  69. package/dist/types/api/index.d.ts.map +1 -1
  70. package/dist/types/api/moneroGetAddress.d.ts +15 -0
  71. package/dist/types/api/moneroGetAddress.d.ts.map +1 -0
  72. package/dist/types/api/moneroGetWatchKey.d.ts +15 -0
  73. package/dist/types/api/moneroGetWatchKey.d.ts.map +1 -0
  74. package/dist/types/api/moneroKeyImageSync.d.ts +24 -0
  75. package/dist/types/api/moneroKeyImageSync.d.ts.map +1 -0
  76. package/dist/types/api/moneroSignTransaction.d.ts +32 -0
  77. package/dist/types/api/moneroSignTransaction.d.ts.map +1 -0
  78. package/dist/types/api/zcashGetAddress.d.ts +22 -0
  79. package/dist/types/api/zcashGetAddress.d.ts.map +1 -0
  80. package/dist/types/api/zcashSignPczt.d.ts +9 -0
  81. package/dist/types/api/zcashSignPczt.d.ts.map +1 -0
  82. package/dist/types/device.d.ts +10 -0
  83. package/dist/types/device.d.ts.map +1 -1
  84. package/dist/utils/logger.d.ts.map +1 -1
  85. package/dist/utils/patch.d.ts +1 -1
  86. package/dist/utils/patch.d.ts.map +1 -1
  87. package/package.json +4 -4
  88. package/src/api/BaseMethod.ts +2 -0
  89. package/src/api/FileWrite.ts +7 -1
  90. package/src/api/FirmwareUpdateV4.ts +358 -74
  91. package/src/api/SearchDevices.ts +172 -7
  92. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +38 -1
  93. package/src/api/firmware/FirmwareArtifactSource.ts +12 -3
  94. package/src/api/helpers/pathUtils.ts +23 -0
  95. package/src/api/helpers/protocolV2FileWrite.ts +375 -111
  96. package/src/api/index.ts +7 -0
  97. package/src/api/monero/MoneroGetAddress.ts +71 -0
  98. package/src/api/monero/MoneroGetWatchKey.ts +69 -0
  99. package/src/api/monero/MoneroKeyImageSync.ts +164 -0
  100. package/src/api/monero/MoneroSignTransaction.ts +319 -0
  101. package/src/api/monero/helpers.ts +28 -0
  102. package/src/api/protocol-v2/DeviceUploadNft.ts +6 -1
  103. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +15 -1
  104. package/src/api/zcash/ZcashGetAddress.ts +119 -0
  105. package/src/api/zcash/ZcashSignPczt.ts +152 -0
  106. package/src/core/index.ts +49 -17
  107. package/src/data/messages/messages-protocol-v2.json +138 -1
  108. package/src/device/Device.ts +21 -3
  109. package/src/device/DeviceConnector.ts +4 -4
  110. package/src/events/logBlockEvent.ts +55 -3
  111. package/src/events/ui-request.ts +2 -0
  112. package/src/index.ts +1 -0
  113. package/src/inject.ts +13 -0
  114. package/src/protocols/protocol-v2/resources.ts +6 -1
  115. package/src/types/api/allNetworkGetAddress.ts +9 -1
  116. package/src/types/api/export.ts +15 -0
  117. package/src/types/api/index.ts +20 -0
  118. package/src/types/api/moneroGetAddress.ts +27 -0
  119. package/src/types/api/moneroGetWatchKey.ts +28 -0
  120. package/src/types/api/moneroKeyImageSync.ts +45 -0
  121. package/src/types/api/moneroSignTransaction.ts +54 -0
  122. package/src/types/api/zcashGetAddress.ts +35 -0
  123. package/src/types/api/zcashSignPczt.ts +18 -0
  124. package/src/types/device.ts +18 -0
  125. package/src/utils/logger.ts +4 -0
@@ -6,7 +6,11 @@ import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
6
6
  import { BaseMethod } from '../BaseMethod';
7
7
  import { decodeJpegBase64ToRgba } from '../helpers/base64Data';
8
8
  import { invalidParameter } from '../helpers/filesystemValidation';
9
- import { writeProtocolV2File } from '../helpers/protocolV2FileWrite';
9
+ import {
10
+ createProtocolV2AssetFileWriteRecovery,
11
+ writeProtocolV2File,
12
+ } from '../helpers/protocolV2FileWrite';
13
+ import { ProtocolV2UiInteractionCoordinator } from '../../protocols/protocol-v2/uiInteraction';
10
14
  import { UI_REQUEST, createUiMessage } from '../../events/ui-request';
11
15
  import { supportsProtocolV2Message } from '../../protocols/protocol-v2/features';
12
16
  import { LoggerNames, getLogger } from '../../utils';
@@ -142,6 +146,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
142
146
  chunkSize: this.params.chunkSize,
143
147
  bleChunkSizeLimit,
144
148
  maxChunkRetries: 3,
149
+ recoverConnection: createProtocolV2AssetFileWriteRecovery(this),
145
150
  overwrite: true,
146
151
  append: false,
147
152
  throwIfAborted: () => this.throwIfAborted(),
@@ -171,6 +176,15 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
171
176
  data,
172
177
  useHostAssetPackage ? WALLPAPER_PACKAGE_BLE_CHUNK_SIZE : undefined
173
178
  );
179
+ this.throwIfAborted();
180
+ new ProtocolV2UiInteractionCoordinator(this.device, this.postMessage).enterMethodInteraction({
181
+ request: 'button',
182
+ source: 'method-lifecycle',
183
+ reason: 'device-management',
184
+ completion: 'operation-completed',
185
+ deviceOnly: true,
186
+ operation: this.name,
187
+ });
174
188
  const response = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
175
189
  settings: { wallpaper_path: this.path },
176
190
  });
@@ -0,0 +1,119 @@
1
+ import { ZcashAddressScope, ZcashAddressType } from '@onekeyfe/hd-transport';
2
+
3
+ import { BaseMethod } from '../BaseMethod';
4
+ import { serializedPath, toHardened, validateStrictPath } from '../helpers/pathUtils';
5
+ import { invalidParameter, validateParams, validateResult } from '../helpers/paramsValidator';
6
+ import { UI_REQUEST } from '../../constants/ui-request';
7
+
8
+ import type { ZcashGetAddress as HardwareZcashGetAddress } from '@onekeyfe/hd-transport';
9
+ import type { ZcashAddress, ZcashGetAddressParams } from '../../types/api/zcashGetAddress';
10
+ import type { DeviceFirmwareRange } from '../../types';
11
+
12
+ export default class ZcashGetAddress extends BaseMethod<HardwareZcashGetAddress[]> {
13
+ hasBundle = false;
14
+
15
+ getSupportedProtocols() {
16
+ return ['V2'] as const;
17
+ }
18
+
19
+ init() {
20
+ this.checkDeviceId = true;
21
+ this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.NOT_INITIALIZE];
22
+ this.strictCheckDeviceSupport = true;
23
+
24
+ this.hasBundle = !!this.payload?.bundle;
25
+ const payload = this.hasBundle ? this.payload : { bundle: [this.payload] };
26
+
27
+ validateParams(payload, [{ name: 'bundle', type: 'array' }]);
28
+
29
+ this.params = [];
30
+ payload.bundle.forEach((batch: ZcashGetAddressParams) => {
31
+ // ZIP-32 account path: m/32'/133'/account'
32
+ const addressN = validateStrictPath(batch.path, 3);
33
+ if (
34
+ addressN.length !== 3 ||
35
+ addressN[0] !== toHardened(32) ||
36
+ addressN[1] !== toHardened(133) ||
37
+ addressN[2] < toHardened(0)
38
+ ) {
39
+ throw invalidParameter("Zcash requires a ZIP-32 account path m/32'/133'/account'");
40
+ }
41
+ const addressType = batch.addressType ?? ZcashAddressType.UNIFIED_ORCHARD_P2PKH;
42
+ const scope = batch.scope ?? ZcashAddressScope.EXTERNAL;
43
+ const diversifierIndex = batch.diversifierIndex ?? 0;
44
+ if (
45
+ ![
46
+ ZcashAddressType.UNIFIED_ORCHARD_P2PKH,
47
+ ZcashAddressType.TRANSPARENT_P2PKH,
48
+ ZcashAddressType.UNIFIED_ORCHARD,
49
+ ].includes(addressType) ||
50
+ ![ZcashAddressScope.EXTERNAL, ZcashAddressScope.INTERNAL].includes(scope)
51
+ ) {
52
+ throw invalidParameter('Invalid Zcash address type or scope');
53
+ }
54
+ const maxIndex = addressType === ZcashAddressType.UNIFIED_ORCHARD ? 0xffffffff : 0x7fffffff;
55
+ if (
56
+ !Number.isSafeInteger(diversifierIndex) ||
57
+ diversifierIndex < 0 ||
58
+ diversifierIndex > maxIndex
59
+ ) {
60
+ throw invalidParameter('Invalid Zcash diversifier index');
61
+ }
62
+
63
+ validateParams(batch, [
64
+ { name: 'path', required: true },
65
+ { name: 'showOnOneKey', type: 'boolean' },
66
+ { name: 'includeUfvk', type: 'boolean' },
67
+ { name: 'includeSeedFingerprint', type: 'boolean' },
68
+ ]);
69
+
70
+ this.params.push({
71
+ address_n: addressN,
72
+ address_type: addressType,
73
+ scope,
74
+ diversifier_index: diversifierIndex,
75
+ show_display: batch.showOnOneKey ?? true,
76
+ include_ufvk: batch.includeUfvk ?? false,
77
+ include_seed_fingerprint: batch.includeSeedFingerprint ?? false,
78
+ });
79
+ });
80
+ }
81
+
82
+ getVersionRange(): DeviceFirmwareRange {
83
+ return {
84
+ model_pro2: {
85
+ min: '0.0.0',
86
+ },
87
+ };
88
+ }
89
+
90
+ async run() {
91
+ const responses: ZcashAddress[] = [];
92
+
93
+ for (let i = 0; i < this.params.length; i++) {
94
+ const param = this.params[i];
95
+
96
+ const res = await this.device.commands.typedCall('ZcashGetAddress', 'ZcashAddress', {
97
+ ...param,
98
+ });
99
+
100
+ const path = serializedPath(param.address_n);
101
+ // The V2 decoder returns null for absent optional fields; omit them instead.
102
+ const { address, ufvk, seed_fingerprint: seedFingerprint } = res.message;
103
+ responses.push({
104
+ path,
105
+ address,
106
+ ...(ufvk ? { ufvk } : {}),
107
+ ...(seedFingerprint ? { seedFingerprint } : {}),
108
+ });
109
+
110
+ this.postPreviousAddressMessage({ address, path });
111
+ }
112
+
113
+ validateResult(responses, ['address'], {
114
+ expectedLength: this.params.length,
115
+ });
116
+
117
+ return Promise.resolve(this.hasBundle ? responses : responses[0]);
118
+ }
119
+ }
@@ -0,0 +1,152 @@
1
+ import { bytesToHex } from '@noble/hashes/utils';
2
+ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
3
+
4
+ import { BaseMethod } from '../BaseMethod';
5
+ import { invalidParameter, validateParams } from '../helpers/paramsValidator';
6
+ import { stripHexPrefix } from '../helpers/hexUtils';
7
+ import { UI_REQUEST } from '../../constants/ui-request';
8
+
9
+ import type { ZcashSignPczt as HardwareZcashSignPczt } from '@onekeyfe/hd-transport';
10
+ import type { TypedCall, TypedResponseMessage } from '../../device/DeviceCommands';
11
+ import type { ZcashSignedPczt } from '../../types/api/zcashSignPczt';
12
+ import type { DeviceFirmwareRange } from '../../types';
13
+
14
+ const CHUNK_BYTE_SIZE = 1024;
15
+ // Host memory sanity bound on the device-announced length, not a device limit.
16
+ const SIGNED_PCZT_SANITY_MAX_BYTES = 16 * 1024 * 1024;
17
+
18
+ export default class ZcashSignPczt extends BaseMethod<HardwareZcashSignPczt> {
19
+ pczt: Buffer = Buffer.alloc(0);
20
+
21
+ getSupportedProtocols() {
22
+ return ['V2'] as const;
23
+ }
24
+
25
+ init() {
26
+ this.checkDeviceId = true;
27
+ this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.NOT_INITIALIZE];
28
+ this.strictCheckDeviceSupport = true;
29
+
30
+ validateParams(this.payload, [{ name: 'pczt', type: 'hexString', required: true }]);
31
+
32
+ // Reject odd-length hex instead of left-padding it, which would shift every nibble.
33
+ const pcztHex = stripHexPrefix(this.payload.pczt);
34
+ if (pcztHex.length % 2 !== 0) {
35
+ throw invalidParameter('pczt hex must have an even length');
36
+ }
37
+ this.pczt = Buffer.from(pcztHex, 'hex');
38
+ if (this.pczt.length === 0) {
39
+ throw invalidParameter('pczt is empty');
40
+ }
41
+
42
+ this.params = {
43
+ pczt_length: this.pczt.length,
44
+ pczt_initial_chunk: bytesToHex(this.pczt.subarray(0, CHUNK_BYTE_SIZE)),
45
+ };
46
+ }
47
+
48
+ getVersionRange(): DeviceFirmwareRange {
49
+ return {
50
+ model_pro2: {
51
+ min: '0.0.0',
52
+ },
53
+ };
54
+ }
55
+
56
+ // Upload: the device decides every chunk length via ZcashPcztChunkRequest and
57
+ // the host must answer with exactly that many bytes.
58
+ uploadPczt = async (
59
+ typedCall: TypedCall,
60
+ first: TypedResponseMessage<'ZcashSignedPczt'> | TypedResponseMessage<'ZcashPcztChunkRequest'>,
61
+ initialOffset: number
62
+ ): Promise<TypedResponseMessage<'ZcashSignedPczt'>> => {
63
+ let res = first;
64
+ let offset = initialOffset;
65
+ while (res.type === 'ZcashPcztChunkRequest') {
66
+ const { chunk_length: chunkLength } = res.message;
67
+ if (
68
+ !Number.isSafeInteger(chunkLength) ||
69
+ chunkLength <= 0 ||
70
+ offset + chunkLength > this.pczt.length
71
+ ) {
72
+ throw ERRORS.TypedError(
73
+ HardwareErrorCode.ResponseUnexpectTypeError,
74
+ `device requested ${chunkLength} bytes at offset ${offset} of ${this.pczt.length}`
75
+ );
76
+ }
77
+
78
+ const chunk = this.pczt.subarray(offset, offset + chunkLength);
79
+ res = await typedCall('ZcashPcztChunkAck', ['ZcashSignedPczt', 'ZcashPcztChunkRequest'], {
80
+ data_chunk: bytesToHex(chunk),
81
+ });
82
+ offset += chunkLength;
83
+ }
84
+
85
+ if (offset !== this.pczt.length) {
86
+ throw ERRORS.TypedError(
87
+ HardwareErrorCode.ResponseUnexpectTypeError,
88
+ 'device completed signing before receiving the whole pczt'
89
+ );
90
+ }
91
+ return res;
92
+ };
93
+
94
+ // Download: the host decides chunk lengths via ZcashSignedPcztChunkRequest
95
+ // until the announced pczt_length has been received.
96
+ downloadSignedPczt = async (
97
+ typedCall: TypedCall,
98
+ res: TypedResponseMessage<'ZcashSignedPczt'>
99
+ ): Promise<Buffer> => {
100
+ const { pczt_length: total, pczt_initial_chunk: initial } = res.message;
101
+ if (!Number.isSafeInteger(total) || total <= 0 || total > SIGNED_PCZT_SANITY_MAX_BYTES) {
102
+ throw ERRORS.TypedError(
103
+ HardwareErrorCode.ResponseUnexpectTypeError,
104
+ `device announced invalid signed pczt length ${total}`
105
+ );
106
+ }
107
+
108
+ const parts: Buffer[] = [Buffer.from(initial ?? '', 'hex')];
109
+ let received = parts[0].length;
110
+ if (received > total) {
111
+ throw ERRORS.TypedError(
112
+ HardwareErrorCode.ResponseUnexpectTypeError,
113
+ 'device returned more initial pczt bytes than announced'
114
+ );
115
+ }
116
+
117
+ while (received < total) {
118
+ const chunkLength = Math.min(CHUNK_BYTE_SIZE, total - received);
119
+ const ack = await typedCall('ZcashSignedPcztChunkRequest', 'ZcashPcztChunkAck', {
120
+ chunk_length: chunkLength,
121
+ });
122
+ const chunk = Buffer.from(ack.message.data_chunk ?? '', 'hex');
123
+ if (chunk.length !== chunkLength) {
124
+ throw ERRORS.TypedError(
125
+ HardwareErrorCode.ResponseUnexpectTypeError,
126
+ `device returned ${chunk.length} of ${chunkLength} requested bytes at offset ${received} of ${total}`
127
+ );
128
+ }
129
+ parts.push(chunk);
130
+ received += chunk.length;
131
+ }
132
+
133
+ return Buffer.concat(parts);
134
+ };
135
+
136
+ async run(): Promise<ZcashSignedPczt> {
137
+ const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
138
+
139
+ const first = await typedCall('ZcashSignPczt', ['ZcashSignedPczt', 'ZcashPcztChunkRequest'], {
140
+ ...this.params,
141
+ });
142
+
143
+ const signed = await this.uploadPczt(
144
+ typedCall,
145
+ first,
146
+ Math.min(CHUNK_BYTE_SIZE, this.pczt.length)
147
+ );
148
+ const pczt = await this.downloadSignedPczt(typedCall, signed);
149
+
150
+ return { pczt: bytesToHex(pczt) };
151
+ }
152
+ }
package/src/core/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import semver from 'semver';
2
+ import { getOwnedUsbDeviceIdentity } from '../api/SearchDevices';
2
3
  import EventEmitter from 'events';
3
4
  import {
4
5
  DeviceSessionPinType,
@@ -60,6 +61,7 @@ import {
60
61
  formatLogMethodLabel,
61
62
  getLogBlockLabel,
62
63
  getSafeLogPayload,
64
+ getSafeLogTask,
63
65
  } from '../events';
64
66
  import TransportManager from '../data-manager/TransportManager';
65
67
  import DeviceConnector from '../device/DeviceConnector';
@@ -125,6 +127,8 @@ const parseInitOptions = (method?: BaseMethod): InitOptions => ({
125
127
  });
126
128
 
127
129
  let _core: Core | undefined;
130
+ let transportSwitchGeneration = 0;
131
+ let pendingTransportSwitch: Promise<void> = Promise.resolve();
128
132
  let _deviceList: DeviceList | undefined;
129
133
  let _connector: DeviceConnector | undefined;
130
134
  let _uiPromises: UiPromise<UiPromiseResponse['type']>[] = []; // Waiting for ui response
@@ -990,15 +994,11 @@ function canSkipInitialize(method: BaseMethod, device: Device): boolean {
990
994
  }
991
995
 
992
996
  export function isRetryableBleProtocolV2ProbeError(method: BaseMethod, error: unknown) {
993
- const typedError = error as { errorCode?: unknown; message?: unknown };
994
- const message =
995
- typeof typedError?.message === 'string' ? typedError.message : String(error ?? '');
997
+ const typedError = error as { errorCode?: unknown; params?: { expectedProtocol?: unknown } };
996
998
  return (
997
999
  method.payload.connectProtocol === 'V2' &&
998
- typedError?.errorCode === HardwareErrorCode.RuntimeError &&
999
- message.includes('Device protocol mismatch') &&
1000
- message.includes('expected V2') &&
1001
- message.includes('did not respond to expected protocol')
1000
+ typedError?.errorCode === HardwareErrorCode.TransportProtocolProbeFailed &&
1001
+ typedError.params?.expectedProtocol === 'V2'
1002
1002
  );
1003
1003
  }
1004
1004
 
@@ -1008,7 +1008,7 @@ export function isRetryableBleConnectionError(method: BaseMethod, error: unknown
1008
1008
  }
1009
1009
  const typedError = error as {
1010
1010
  errorCode?: unknown;
1011
- params?: { acquireDeadlineExceeded?: unknown };
1011
+ params?: { acquireDeadlineExceeded?: unknown; expectedProtocol?: unknown };
1012
1012
  };
1013
1013
  if (
1014
1014
  typedError?.errorCode === HardwareErrorCode.BleTimeoutError &&
@@ -1019,6 +1019,8 @@ export function isRetryableBleConnectionError(method: BaseMethod, error: unknown
1019
1019
  return (
1020
1020
  typedError?.errorCode === HardwareErrorCode.BleTimeoutError ||
1021
1021
  typedError?.errorCode === HardwareErrorCode.BleConnectedError ||
1022
+ (typedError?.errorCode === HardwareErrorCode.TransportProtocolProbeFailed &&
1023
+ !typedError.params?.expectedProtocol) ||
1022
1024
  isRetryableBleProtocolV2ProbeError(method, error) ||
1023
1025
  isMissingDetectedProtocolV2Error(method, error)
1024
1026
  );
@@ -1273,6 +1275,8 @@ const ensureConnected = async (
1273
1275
  HardwareErrorCode.BridgeTimeoutError,
1274
1276
  HardwareErrorCode.BridgeNeedsPermission,
1275
1277
  HardwareErrorCode.WebUsbDeviceAccessError,
1278
+ HardwareErrorCode.TransportProtocolProbeFailed,
1279
+ HardwareErrorCode.TransportProtocolMismatch,
1276
1280
  ].includes(error.errorCode)
1277
1281
  ) {
1278
1282
  _deviceList = undefined;
@@ -1360,6 +1364,8 @@ const ensureConnected = async (
1360
1364
  HardwareErrorCode.BleCharacteristicNotifyChangeFailure,
1361
1365
  HardwareErrorCode.BridgeNeedsPermission,
1362
1366
  HardwareErrorCode.WebUsbDeviceAccessError,
1367
+ HardwareErrorCode.TransportProtocolProbeFailed,
1368
+ HardwareErrorCode.TransportProtocolMismatch,
1363
1369
  HardwareErrorCode.DeviceInterruptedFromUser,
1364
1370
  HardwareErrorCode.CallQueueActionCancelled,
1365
1371
  ].includes(error.errorCode) ||
@@ -1446,7 +1452,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1446
1452
  };
1447
1453
  for (const requestId of requestIds) {
1448
1454
  const task = requestQueue.getTask(requestId);
1449
- Log.debug('Cancel Api connect task: ', task);
1455
+ Log.debug('Cancel Api connect task: ', getSafeLogTask(task));
1450
1456
  if (task) {
1451
1457
  // During ensureConnected the method has a connectId but device is
1452
1458
  // assigned only after the poll succeeds. Interrupt the cached BLE
@@ -1481,7 +1487,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1481
1487
  };
1482
1488
  for (const requestId of requestQueue.getRequestTasksId()) {
1483
1489
  const task = requestQueue.getTask(requestId);
1484
- Log.debug('Cancel Api connect task: ', task);
1490
+ Log.debug('Cancel Api connect task: ', getSafeLogTask(task));
1485
1491
  if (task) {
1486
1492
  interruptDevice(task.method?.device);
1487
1493
  if (task.method.connectId) {
@@ -1826,6 +1832,10 @@ export default class Core extends EventEmitter {
1826
1832
 
1827
1833
  private requestQueue = new RequestQueue();
1828
1834
 
1835
+ getOwnedUsbDeviceIdentity(descriptor: Parameters<typeof getOwnedUsbDeviceIdentity>[0]) {
1836
+ return getOwnedUsbDeviceIdentity(descriptor, this.requestQueue);
1837
+ }
1838
+
1829
1839
  private disposePromise?: Promise<void>;
1830
1840
 
1831
1841
  // background task
@@ -1940,7 +1950,7 @@ export default class Core extends EventEmitter {
1940
1950
  break;
1941
1951
  }
1942
1952
  case IFRAME.CALLBACK: {
1943
- Log.log('callback message: ', message);
1953
+ Log.log('callback message: ', getSafeLogPayload(message));
1944
1954
  postMessage(message);
1945
1955
  break;
1946
1956
  }
@@ -1959,6 +1969,8 @@ export default class Core extends EventEmitter {
1959
1969
 
1960
1970
  private async disposeResources(): Promise<void> {
1961
1971
  Log.debug(`[Core] Disposing SDK instance: ${this.sdkInstanceId}`);
1972
+ transportSwitchGeneration += 1;
1973
+ pendingTransportSwitch = Promise.resolve();
1962
1974
  pollingManager.stopAll();
1963
1975
  this.requestQueue.abortAllRequests();
1964
1976
  cleanup();
@@ -1993,6 +2005,8 @@ export default class Core extends EventEmitter {
1993
2005
  }
1994
2006
 
1995
2007
  export const initCore = () => {
2008
+ transportSwitchGeneration += 1;
2009
+ pendingTransportSwitch = Promise.resolve();
1996
2010
  const core = new Core();
1997
2011
  _core = core;
1998
2012
  return core;
@@ -2022,6 +2036,8 @@ export const init = async (
2022
2036
  plugin?: LowlevelTransportSharedPlugin
2023
2037
  ) => {
2024
2038
  try {
2039
+ transportSwitchGeneration += 1;
2040
+ pendingTransportSwitch = Promise.resolve();
2025
2041
  await DataManager.load(settings);
2026
2042
  initTransport(Transport, plugin);
2027
2043
  enableLog(DataManager.getSettings('debug'));
@@ -2047,10 +2063,26 @@ export const switchTransport = ({
2047
2063
  Transport: any;
2048
2064
  plugin?: LowlevelTransportSharedPlugin;
2049
2065
  }) => {
2050
- DataManager.updateEnv(env);
2051
- TransportManager.setTransport(Transport, plugin);
2052
- _deviceList = undefined;
2053
- DevicePool.resetState();
2054
- _connector = undefined;
2055
- initConnector();
2066
+ const generation = transportSwitchGeneration;
2067
+ // Preserve request order while native transport cleanup is pending.
2068
+ const switching = pendingTransportSwitch
2069
+ .catch(() => undefined)
2070
+ .then(async () => {
2071
+ if (generation !== transportSwitchGeneration) {
2072
+ throw ERRORS.TypedError(HardwareErrorCode.NotInitialized);
2073
+ }
2074
+ _connector?.stop();
2075
+ await TransportManager.getTransport()?.stop?.();
2076
+ if (generation !== transportSwitchGeneration) {
2077
+ throw ERRORS.TypedError(HardwareErrorCode.NotInitialized);
2078
+ }
2079
+ DataManager.updateEnv(env);
2080
+ TransportManager.setTransport(Transport, plugin);
2081
+ _deviceList = undefined;
2082
+ DevicePool.resetState();
2083
+ _connector = undefined;
2084
+ initConnector();
2085
+ });
2086
+ pendingTransportSwitch = switching;
2087
+ return switching;
2056
2088
  };
@@ -11034,6 +11034,61 @@
11034
11034
  }
11035
11035
  }
11036
11036
  },
11037
+ "ZcashSignPczt": {
11038
+ "fields": {
11039
+ "pczt_length": {
11040
+ "rule": "required",
11041
+ "type": "uint32",
11042
+ "id": 1
11043
+ },
11044
+ "pczt_initial_chunk": {
11045
+ "rule": "required",
11046
+ "type": "bytes",
11047
+ "id": 2
11048
+ }
11049
+ }
11050
+ },
11051
+ "ZcashPcztChunkRequest": {
11052
+ "fields": {
11053
+ "chunk_length": {
11054
+ "rule": "required",
11055
+ "type": "uint32",
11056
+ "id": 1
11057
+ }
11058
+ }
11059
+ },
11060
+ "ZcashPcztChunkAck": {
11061
+ "fields": {
11062
+ "data_chunk": {
11063
+ "rule": "required",
11064
+ "type": "bytes",
11065
+ "id": 1
11066
+ }
11067
+ }
11068
+ },
11069
+ "ZcashSignedPczt": {
11070
+ "fields": {
11071
+ "pczt_length": {
11072
+ "rule": "required",
11073
+ "type": "uint32",
11074
+ "id": 1
11075
+ },
11076
+ "pczt_initial_chunk": {
11077
+ "rule": "required",
11078
+ "type": "bytes",
11079
+ "id": 2
11080
+ }
11081
+ }
11082
+ },
11083
+ "ZcashSignedPcztChunkRequest": {
11084
+ "fields": {
11085
+ "chunk_length": {
11086
+ "rule": "required",
11087
+ "type": "uint32",
11088
+ "id": 1
11089
+ }
11090
+ }
11091
+ },
11037
11092
  "wire_in": {
11038
11093
  "type": "bool",
11039
11094
  "id": 50002,
@@ -11459,6 +11514,11 @@
11459
11514
  "MessageType_NeoAddress": 12302,
11460
11515
  "MessageType_NeoSignTx": 12303,
11461
11516
  "MessageType_NeoSignedTx": 12304,
11517
+ "MessageType_ZcashSignPczt": 12402,
11518
+ "MessageType_ZcashSignedPczt": 12403,
11519
+ "MessageType_ZcashPcztChunkRequest": 12404,
11520
+ "MessageType_ZcashPcztChunkAck": 12405,
11521
+ "MessageType_ZcashSignedPcztChunkRequest": 12406,
11462
11522
  "MessageType_DeviceFactoryInfoSet": 60000,
11463
11523
  "MessageType_DeviceFactoryInfoGet": 60001,
11464
11524
  "MessageType_DeviceFactoryInfo": 60002,
@@ -11511,7 +11571,9 @@
11511
11571
  "MessageType_PortfolioUpdate": 61400,
11512
11572
  "MessageType_NftUpdate": 61500,
11513
11573
  "MessageType_OnboardingStatusGet": 61600,
11514
- "MessageType_OnboardingStatus": 61601
11574
+ "MessageType_OnboardingStatus": 61601,
11575
+ "MessageType_ZcashGetAddress": 12400,
11576
+ "MessageType_ZcashAddress": 12401
11515
11577
  },
11516
11578
  "reserved": [
11517
11579
  [90, 92],
@@ -13730,6 +13792,81 @@
13730
13792
  }
13731
13793
  }
13732
13794
  }
13795
+ },
13796
+ "ZcashAddressType": {
13797
+ "values": {
13798
+ "UNIFIED_ORCHARD_P2PKH": 0,
13799
+ "TRANSPARENT_P2PKH": 1,
13800
+ "UNIFIED_ORCHARD": 2
13801
+ }
13802
+ },
13803
+ "ZcashAddressScope": {
13804
+ "values": {
13805
+ "EXTERNAL": 0,
13806
+ "INTERNAL": 1
13807
+ }
13808
+ },
13809
+ "ZcashGetAddress": {
13810
+ "fields": {
13811
+ "address_n": {
13812
+ "rule": "repeated",
13813
+ "type": "uint32",
13814
+ "id": 1,
13815
+ "options": {
13816
+ "packed": false
13817
+ }
13818
+ },
13819
+ "show_display": {
13820
+ "type": "bool",
13821
+ "id": 2
13822
+ },
13823
+ "include_seed_fingerprint": {
13824
+ "type": "bool",
13825
+ "id": 3
13826
+ },
13827
+ "include_ufvk": {
13828
+ "type": "bool",
13829
+ "id": 4
13830
+ },
13831
+ "diversifier_index": {
13832
+ "type": "uint32",
13833
+ "id": 5,
13834
+ "options": {
13835
+ "default": 0
13836
+ }
13837
+ },
13838
+ "address_type": {
13839
+ "type": "ZcashAddressType",
13840
+ "id": 6,
13841
+ "options": {
13842
+ "default": "UNIFIED_ORCHARD_P2PKH"
13843
+ }
13844
+ },
13845
+ "scope": {
13846
+ "type": "ZcashAddressScope",
13847
+ "id": 7,
13848
+ "options": {
13849
+ "default": "EXTERNAL"
13850
+ }
13851
+ }
13852
+ }
13853
+ },
13854
+ "ZcashAddress": {
13855
+ "fields": {
13856
+ "address": {
13857
+ "rule": "required",
13858
+ "type": "string",
13859
+ "id": 1
13860
+ },
13861
+ "ufvk": {
13862
+ "type": "string",
13863
+ "id": 2
13864
+ },
13865
+ "seed_fingerprint": {
13866
+ "type": "bytes",
13867
+ "id": 3
13868
+ }
13869
+ }
13733
13870
  }
13734
13871
  }
13735
13872
  }