@onekeyfe/hd-core 1.2.8-alpha.17 → 1.2.8-alpha.19

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.
@@ -1,4 +1,4 @@
1
- import { DeviceType } from '@onekeyfe/hd-transport';
1
+ import protocolTransport, { DeviceType } from '@onekeyfe/hd-transport';
2
2
  import { EDeviceType } from '@onekeyfe/hd-shared';
3
3
 
4
4
  import {
@@ -148,6 +148,41 @@ describe('isolated USB identity probe', () => {
148
148
  );
149
149
  });
150
150
 
151
+ test.each([
152
+ { onekey_device_type: 'CLASSIC', major_version: 2, minor_version: 8, patch_version: 0 },
153
+ { onekey_device_type: 'TOUCH', major_version: 4, minor_version: 4, patch_version: 0 },
154
+ ])(
155
+ 'preserves GetFeatures identity when the legacy V1 schema has no vendor query (%s)',
156
+ async firmware => {
157
+ const transport = createTransport();
158
+ let messages = protocolTransport.parseConfigure(
159
+ JSON.stringify(DataManager.getProtobufMessages())
160
+ );
161
+ transport.configure.mockImplementation((schema: string) => {
162
+ messages = protocolTransport.parseConfigure(schema);
163
+ });
164
+ transport.call.mockImplementation((_session, name, data) => {
165
+ protocolTransport.ProtocolV1.encodeMessageChunks(messages, name, data);
166
+ return Promise.resolve({
167
+ type: 'Features',
168
+ message: { ...firmware, device_id: 'LEGACY_WALLET_ID' },
169
+ });
170
+ });
171
+ await expect(
172
+ probeUsbDeviceIdentitiesWithTransport(transport, 'webusb')
173
+ ).resolves.toMatchObject([
174
+ {
175
+ path: descriptor.path,
176
+ serialNo: null,
177
+ deviceId: 'LEGACY_WALLET_ID',
178
+ connectProtocol: 'V1',
179
+ },
180
+ ]);
181
+ expect(transport.call.mock.calls.map(call => call[1])).toEqual(['GetFeatures']);
182
+ expect(transport.release).toHaveBeenCalledWith('usb-transport-handle', false);
183
+ }
184
+ );
185
+
151
186
  test('does not substitute a USB path for a missing business identity', async () => {
152
187
  const transport = createTransport();
153
188
  transport.call
@@ -171,6 +206,72 @@ describe('isolated USB identity probe', () => {
171
206
  expect(transport.release).not.toHaveBeenCalled();
172
207
  });
173
208
 
209
+ test('retries its own failed Bridge release before probing again', async () => {
210
+ const transport = createTransport();
211
+ let held: string | undefined;
212
+ transport.enumerate.mockImplementation(() =>
213
+ Promise.resolve([{ ...descriptor, commType: 'bridge', session: held }])
214
+ );
215
+ transport.acquire.mockImplementation(() => {
216
+ held = 'probe-transport-handle';
217
+ return Promise.resolve(held);
218
+ });
219
+ transport.release
220
+ .mockRejectedValueOnce(new Error('release request lost'))
221
+ .mockRejectedValueOnce(new Error('Bridge still unavailable'))
222
+ .mockImplementation(() => {
223
+ held = undefined;
224
+ return Promise.resolve(undefined);
225
+ });
226
+
227
+ await expect(probeUsbDeviceIdentitiesWithTransport(transport, 'bridge')).resolves.toEqual([]);
228
+ await expect(probeUsbDeviceIdentitiesWithTransport(transport, 'bridge')).resolves.toEqual([]);
229
+ expect(transport.acquire).toHaveBeenCalledTimes(1);
230
+ expect(transport.call).toHaveBeenCalledTimes(1);
231
+ await expect(probeUsbDeviceIdentitiesWithTransport(transport, 'bridge')).resolves.toHaveLength(
232
+ 1
233
+ );
234
+ expect(transport.acquire).toHaveBeenCalledTimes(2);
235
+ expect(transport.release).toHaveBeenCalledTimes(4);
236
+ });
237
+
238
+ test.each([undefined, 'foreign-transport-handle'])(
239
+ 'does not retry a stale Bridge release when ownership changes to %s',
240
+ async replacement => {
241
+ const transport = createTransport();
242
+ transport.enumerate.mockResolvedValue([{ ...descriptor, commType: 'bridge' }]);
243
+ transport.release.mockRejectedValueOnce(new Error('release response lost'));
244
+ await probeUsbDeviceIdentitiesWithTransport(transport, 'bridge');
245
+ transport.enumerate.mockResolvedValue([
246
+ { ...descriptor, commType: 'bridge', session: replacement },
247
+ ]);
248
+ transport.release.mockClear();
249
+ const result = await probeUsbDeviceIdentitiesWithTransport(transport, 'bridge');
250
+ expect(result).toHaveLength(replacement ? 0 : 1);
251
+ expect(transport.release).toHaveBeenCalledTimes(replacement ? 0 : 1);
252
+ if (replacement) {
253
+ transport.enumerate.mockResolvedValue([{ ...descriptor, commType: 'bridge' }]);
254
+ await expect(
255
+ probeUsbDeviceIdentitiesWithTransport(transport, 'bridge')
256
+ ).resolves.toHaveLength(1);
257
+ }
258
+ }
259
+ );
260
+
261
+ test('never cleans up a Bridge handle recorded by another transport instance', async () => {
262
+ const transport = createTransport();
263
+ transport.enumerate.mockResolvedValue([{ ...descriptor, commType: 'bridge' }]);
264
+ transport.release.mockRejectedValueOnce(new Error('release request lost'));
265
+ await probeUsbDeviceIdentitiesWithTransport(transport, 'bridge');
266
+ const other = createTransport();
267
+ other.enumerate.mockResolvedValue([
268
+ { ...descriptor, commType: 'bridge', session: 'usb-transport-handle' },
269
+ ]);
270
+ await expect(probeUsbDeviceIdentitiesWithTransport(other, 'bridge')).resolves.toEqual([]);
271
+ expect(other.release).not.toHaveBeenCalled();
272
+ expect(other.acquire).not.toHaveBeenCalled();
273
+ });
274
+
174
275
  test('reuses identity only for the exact active WebUSB handle', () => {
175
276
  const handle = {};
176
277
  const endpoint = { ...descriptor, device: handle };
@@ -1 +1 @@
1
- {"version":3,"file":"SearchDevices.d.ts","sourceRoot":"","sources":["../../src/api/SearchDevices.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAU1C,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,KAAK,eAAe,MAAM,2BAA2B,CAAC;AAC7D,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,IAAI,gBAAgB,EAEpC,SAAS,EACV,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI/C,KAAK,kBAAkB,GAAG;IACxB,4BAA4B,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAC9D,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK;QAAE,MAAM,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,SAAS,CAAC;CAC7E,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,UAAU,CAAC,GAClF,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC;AA4CpD,eAAO,MAAM,yBAAyB,eACxB,0BAA0B,gBACxB,kBAAkB,KAC/B,iBAAiB,GAAG,SAYtB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAC1C,SAAS,EACT,SAAS,GAAG,MAAM,GAAG,iBAAiB,GAAG,qBAAqB,CAC/D,GAAG;IACF,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,SAAS,EAAE,MAAM,OAAO,CAAC,0BAA0B,EAAE,CAAC,CAAC;IACvD,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC9D,SAAS,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD,CAAC;AAYF,eAAO,MAAM,qCAAqC,cACrC,yBAAyB,YAC1B,iBAAiB,CAAC,UAAU,CAAC,mCACP,0BAA0B,KAAK,iBAAiB,GAAG,SAAS,kBAC3F,QAAQ,iBAAiB,EAAE,CAgG7B,CAAC;AAsBF,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,UAAU;IACnD,SAAS,CAAC,EAAE,eAAe,CAAa;IAExC,IAAI;IAME,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;CAuFrC"}
1
+ {"version":3,"file":"SearchDevices.d.ts","sourceRoot":"","sources":["../../src/api/SearchDevices.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAU1C,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,KAAK,eAAe,MAAM,2BAA2B,CAAC;AAC7D,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,IAAI,gBAAgB,EAEpC,SAAS,EACV,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI/C,KAAK,kBAAkB,GAAG;IACxB,4BAA4B,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAC9D,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK;QAAE,MAAM,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,SAAS,CAAC;CAC7E,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,UAAU,CAAC,GAClF,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC;AA4CpD,eAAO,MAAM,yBAAyB,eACxB,0BAA0B,gBACxB,kBAAkB,KAC/B,iBAAiB,GAAG,SAYtB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAC1C,SAAS,EACT,SAAS,GAAG,MAAM,GAAG,iBAAiB,GAAG,qBAAqB,CAC/D,GAAG;IACF,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,SAAS,EAAE,MAAM,OAAO,CAAC,0BAA0B,EAAE,CAAC,CAAC;IACvD,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC9D,SAAS,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD,CAAC;AAcF,eAAO,MAAM,qCAAqC,cACrC,yBAAyB,YAC1B,iBAAiB,CAAC,UAAU,CAAC,mCACP,0BAA0B,KAAK,iBAAiB,GAAG,SAAS,kBAC3F,QAAQ,iBAAiB,EAAE,CAgI7B,CAAC;AAsBF,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,UAAU;IACnD,SAAS,CAAC,EAAE,eAAe,CAAa;IAExC,IAAI;IAME,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;CAuFrC"}
package/dist/index.js CHANGED
@@ -47231,6 +47231,7 @@ const identityProbeLogger = {
47231
47231
  warn: () => undefined,
47232
47232
  error: () => undefined,
47233
47233
  };
47234
+ const pendingBridgeProbeReleases = new WeakMap();
47234
47235
  const probeUsbDeviceIdentitiesWithTransport = (transport, commType, getOwnedIdentity) => __awaiter(void 0, void 0, void 0, function* () {
47235
47236
  var _a;
47236
47237
  yield transport.init(identityProbeLogger);
@@ -47238,6 +47239,14 @@ const probeUsbDeviceIdentitiesWithTransport = (transport, commType, getOwnedIden
47238
47239
  yield transport.configure(defaultMessages);
47239
47240
  yield ((_a = transport.configureProtocolV2) === null || _a === void 0 ? void 0 : _a.call(transport, JSON.stringify(DataManager.getProtobufMessages('v2Schema'))));
47240
47241
  const descriptors = yield transport.enumerate();
47242
+ let pendingReleases = pendingBridgeProbeReleases.get(transport);
47243
+ if (commType === 'bridge' && pendingReleases) {
47244
+ for (const [path, session] of pendingReleases) {
47245
+ if (!descriptors.some(descriptor => descriptor.path === path && descriptor.session === session)) {
47246
+ pendingReleases.delete(path);
47247
+ }
47248
+ }
47249
+ }
47241
47250
  const identities = [];
47242
47251
  const readIdentity = (descriptor) => __awaiter(void 0, void 0, void 0, function* () {
47243
47252
  var _b, _c;
@@ -47246,6 +47255,17 @@ const probeUsbDeviceIdentitiesWithTransport = (transport, commType, getOwnedIden
47246
47255
  return undefined;
47247
47256
  if (descriptors.filter(candidate => candidate.path === path).length !== 1)
47248
47257
  return undefined;
47258
+ const pendingSession = commType === 'bridge' ? pendingReleases === null || pendingReleases === void 0 ? void 0 : pendingReleases.get(path) : undefined;
47259
+ if (pendingSession && descriptor.session === pendingSession) {
47260
+ try {
47261
+ yield transport.release(pendingSession, false);
47262
+ pendingReleases === null || pendingReleases === void 0 ? void 0 : pendingReleases.delete(path);
47263
+ descriptor = Object.assign(Object.assign({}, descriptor), { session: undefined });
47264
+ }
47265
+ catch (_d) {
47266
+ return undefined;
47267
+ }
47268
+ }
47249
47269
  const owner = getOwnedIdentity === null || getOwnedIdentity === void 0 ? void 0 : getOwnedIdentity(descriptor);
47250
47270
  if (owner) {
47251
47271
  return owner;
@@ -47266,12 +47286,14 @@ const probeUsbDeviceIdentitiesWithTransport = (transport, commType, getOwnedIden
47266
47286
  return undefined;
47267
47287
  let features = buildProtocolV1FeaturesPayload(response.message);
47268
47288
  if (!features.serialNo) {
47269
- const { messages } = getSupportProtocolV1MessageSchema(features);
47270
- if (messages)
47271
- yield transport.configure(JSON.stringify(messages));
47272
- const vendor = yield transport.call(session, 'OnekeyGetFeatures', {}, { timeoutMs: 3000 });
47273
- if (vendor.type === 'OnekeyFeatures') {
47274
- features = buildProtocolV1FeaturesPayload(Object.assign(Object.assign({}, response.message), vendor.message));
47289
+ const { messages, protocolV1MessageSchema } = getSupportProtocolV1MessageSchema(features);
47290
+ if (protocolV1MessageSchema === 'v1CurrentSchema') {
47291
+ if (messages)
47292
+ yield transport.configure(JSON.stringify(messages));
47293
+ const vendor = yield transport.call(session, 'OnekeyGetFeatures', {}, { timeoutMs: 3000 });
47294
+ if (vendor.type === 'OnekeyFeatures') {
47295
+ features = buildProtocolV1FeaturesPayload(Object.assign(Object.assign({}, response.message), vendor.message));
47296
+ }
47275
47297
  }
47276
47298
  }
47277
47299
  identified = {
@@ -47298,15 +47320,22 @@ const probeUsbDeviceIdentitiesWithTransport = (transport, commType, getOwnedIden
47298
47320
  };
47299
47321
  }
47300
47322
  }
47301
- catch (_d) {
47323
+ catch (_e) {
47302
47324
  }
47303
47325
  finally {
47304
47326
  if (session) {
47305
47327
  try {
47306
47328
  yield transport.release(session, false);
47307
47329
  }
47308
- catch (_e) {
47330
+ catch (_f) {
47309
47331
  identified = undefined;
47332
+ if (commType === 'bridge') {
47333
+ if (!pendingReleases) {
47334
+ pendingReleases = new Map();
47335
+ pendingBridgeProbeReleases.set(transport, pendingReleases);
47336
+ }
47337
+ pendingReleases.set(path, session);
47338
+ }
47310
47339
  }
47311
47340
  }
47312
47341
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-core",
3
- "version": "1.2.8-alpha.17",
3
+ "version": "1.2.8-alpha.19",
4
4
  "description": "Core processes and APIs for communicating with OneKey hardware devices.",
5
5
  "author": "OneKey",
6
6
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
@@ -25,8 +25,8 @@
25
25
  "url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
26
26
  },
27
27
  "dependencies": {
28
- "@onekeyfe/hd-shared": "1.2.8-alpha.17",
29
- "@onekeyfe/hd-transport": "1.2.8-alpha.17",
28
+ "@onekeyfe/hd-shared": "1.2.8-alpha.19",
29
+ "@onekeyfe/hd-transport": "1.2.8-alpha.19",
30
30
  "axios": "1.15.2",
31
31
  "bignumber.js": "^9.0.2",
32
32
  "buffer": "^6.0.3",
@@ -46,5 +46,5 @@
46
46
  "@types/w3c-web-usb": "^1.0.10",
47
47
  "@types/web-bluetooth": "^0.0.21"
48
48
  },
49
- "gitHead": "d7376861ee755a4c65226468a6b67ab2e6e9c55d"
49
+ "gitHead": "822e744bceb68a0928ad89d445b985897432edcf"
50
50
  }
@@ -109,6 +109,8 @@ const identityProbeLogger = {
109
109
  error: () => undefined,
110
110
  };
111
111
 
112
+ const pendingBridgeProbeReleases = new WeakMap<UsbIdentityProbeTransport, Map<string, string>>();
113
+
112
114
  /** Runs on a separate transport; never initializes or switches the current Core. */
113
115
  export const probeUsbDeviceIdentitiesWithTransport = async (
114
116
  transport: UsbIdentityProbeTransport,
@@ -122,6 +124,16 @@ export const probeUsbDeviceIdentitiesWithTransport = async (
122
124
  JSON.stringify(DataManager.getProtobufMessages('v2Schema'))
123
125
  );
124
126
  const descriptors = await transport.enumerate();
127
+ let pendingReleases = pendingBridgeProbeReleases.get(transport);
128
+ if (commType === 'bridge' && pendingReleases) {
129
+ for (const [path, session] of pendingReleases) {
130
+ if (
131
+ !descriptors.some(descriptor => descriptor.path === path && descriptor.session === session)
132
+ ) {
133
+ pendingReleases.delete(path);
134
+ }
135
+ }
136
+ }
125
137
  const identities: UsbDeviceIdentity[] = [];
126
138
  const readIdentity = async (
127
139
  descriptor: UsbIdentityProbeDescriptor
@@ -129,6 +141,16 @@ export const probeUsbDeviceIdentitiesWithTransport = async (
129
141
  const { path } = descriptor;
130
142
  if (!path || descriptor.commType !== commType) return undefined;
131
143
  if (descriptors.filter(candidate => candidate.path === path).length !== 1) return undefined;
144
+ const pendingSession = commType === 'bridge' ? pendingReleases?.get(path) : undefined;
145
+ if (pendingSession && descriptor.session === pendingSession) {
146
+ try {
147
+ await transport.release(pendingSession, false);
148
+ pendingReleases?.delete(path);
149
+ descriptor = { ...descriptor, session: undefined };
150
+ } catch {
151
+ return undefined;
152
+ }
153
+ }
132
154
  const owner = getOwnedIdentity?.(descriptor);
133
155
  if (owner) {
134
156
  return owner;
@@ -149,19 +171,22 @@ export const probeUsbDeviceIdentitiesWithTransport = async (
149
171
  if (response.type !== 'Features') return undefined;
150
172
  let features = buildProtocolV1FeaturesPayload(response.message as PROTO.Features);
151
173
  if (!features.serialNo) {
152
- const { messages } = getSupportProtocolV1MessageSchema(features);
153
- if (messages) await transport.configure(JSON.stringify(messages));
154
- const vendor = await transport.call(
155
- session,
156
- 'OnekeyGetFeatures',
157
- {},
158
- { timeoutMs: 3000 }
159
- );
160
- if (vendor.type === 'OnekeyFeatures') {
161
- features = buildProtocolV1FeaturesPayload({
162
- ...response.message,
163
- ...vendor.message,
164
- } as PROTO.Features);
174
+ const { messages, protocolV1MessageSchema } = getSupportProtocolV1MessageSchema(features);
175
+ // Legacy V1 has no OnekeyGetFeatures; keep the identity confirmed by GetFeatures.
176
+ if (protocolV1MessageSchema === 'v1CurrentSchema') {
177
+ if (messages) await transport.configure(JSON.stringify(messages));
178
+ const vendor = await transport.call(
179
+ session,
180
+ 'OnekeyGetFeatures',
181
+ {},
182
+ { timeoutMs: 3000 }
183
+ );
184
+ if (vendor.type === 'OnekeyFeatures') {
185
+ features = buildProtocolV1FeaturesPayload({
186
+ ...response.message,
187
+ ...vendor.message,
188
+ } as PROTO.Features);
189
+ }
165
190
  }
166
191
  }
167
192
  identified = {
@@ -200,6 +225,15 @@ export const probeUsbDeviceIdentitiesWithTransport = async (
200
225
  await transport.release(session, false);
201
226
  } catch {
202
227
  identified = undefined;
228
+ // Retain only our own Bridge handle; enumeration must confirm ownership
229
+ // before a later probe retries a release whose HTTP request may be lost.
230
+ if (commType === 'bridge') {
231
+ if (!pendingReleases) {
232
+ pendingReleases = new Map();
233
+ pendingBridgeProbeReleases.set(transport, pendingReleases);
234
+ }
235
+ pendingReleases.set(path, session);
236
+ }
203
237
  }
204
238
  }
205
239
  }