@onekeyfe/hd-core 1.2.2-alpha.7 → 1.2.2-alpha.9

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.
@@ -2,6 +2,7 @@ import { EDeviceType, ERRORS, HardwareErrorCode, createDeferred } from '@onekeyf
2
2
  import { DeviceType, TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
3
3
 
4
4
  import {
5
+ cancel,
5
6
  initConnector,
6
7
  initCore,
7
8
  isMissingDetectedProtocolV2Error,
@@ -120,6 +121,42 @@ describe('public device lifecycle events', () => {
120
121
  expect(context.getPrePendingCallPromise('device-a')).toBe(replacementCleanup.promise);
121
122
  });
122
123
 
124
+ test('cancels only requests associated with the requested connect id', () => {
125
+ core = initCore();
126
+ const context = (core as any).getCoreContext();
127
+ const deviceA = {
128
+ mainId: 'transport-session-a',
129
+ getConnectId: jest.fn(() => 'serial-a'),
130
+ interruptionFromUser: jest.fn().mockResolvedValue(undefined),
131
+ };
132
+ const deviceB = {
133
+ mainId: 'device-b',
134
+ getConnectId: jest.fn(() => 'serial-b'),
135
+ interruptionFromUser: jest.fn().mockResolvedValue(undefined),
136
+ };
137
+ const taskA = context.requestQueue.createTask({
138
+ responseID: 101,
139
+ connectId: '',
140
+ device: deviceA,
141
+ } as never);
142
+ const taskB = context.requestQueue.createTask({
143
+ responseID: 102,
144
+ connectId: 'device-b',
145
+ device: deviceB,
146
+ } as never);
147
+ const signalA = taskA.abortController?.signal;
148
+ const signalB = taskB.abortController?.signal;
149
+
150
+ cancel(context, 'serial-a');
151
+
152
+ expect(signalA?.aborted).toBe(true);
153
+ expect(context.requestQueue.getTask(taskA.id)).toBeUndefined();
154
+ expect(signalB?.aborted).toBe(false);
155
+ expect(context.requestQueue.getTask(taskB.id)).toBe(taskB);
156
+ expect(deviceA.interruptionFromUser).toHaveBeenCalledTimes(1);
157
+ expect(deviceB.interruptionFromUser).not.toHaveBeenCalled();
158
+ });
159
+
123
160
  test('keeps shared device lifecycle listeners across a device cache reset', () => {
124
161
  jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
125
162
  core = initCore();
@@ -26,12 +26,16 @@ jest.mock('../src/data/config', () => ({
26
26
  const createDevice = ({
27
27
  passphraseProtection = true,
28
28
  unlockedAttachPin = false,
29
+ refreshedUnlocked = true,
30
+ refreshedPassphraseProtection = passphraseProtection,
29
31
  refreshedUnlockedAttachPin = unlockedAttachPin,
30
32
  typedCall = jest.fn(),
31
33
  promptPassphrase = jest.fn(),
32
34
  }: {
33
35
  passphraseProtection?: boolean;
34
36
  unlockedAttachPin?: boolean;
37
+ refreshedUnlocked?: boolean;
38
+ refreshedPassphraseProtection?: boolean;
35
39
  refreshedUnlockedAttachPin?: boolean;
36
40
  typedCall?: jest.Mock;
37
41
  promptPassphrase?: jest.Mock;
@@ -50,11 +54,11 @@ const createDevice = ({
50
54
  return {
51
55
  message: {
52
56
  device_id: 'device-1',
53
- unlocked: true,
57
+ unlocked: refreshedUnlocked,
54
58
  attach_to_pin_enabled: unlockedAttachPin,
55
59
  unlocked_attach_pin: refreshedUnlockedAttachPin,
56
60
  unlocked_by_attach_to_pin: refreshedUnlockedAttachPin,
57
- passphrase_enabled: passphraseProtection,
61
+ passphrase_enabled: refreshedPassphraseProtection,
58
62
  },
59
63
  };
60
64
  }
@@ -95,6 +99,8 @@ const createDevice = ({
95
99
  unlockDevice: jest.fn(),
96
100
  updateProtocolV2Status: jest.fn((status: Record<string, unknown>) => {
97
101
  device.features.unlocked = status.unlocked ?? device.features.unlocked;
102
+ device.features.passphraseProtection =
103
+ status.passphrase_enabled ?? device.features.passphraseProtection;
98
104
  device.features.attachToPinEnabled =
99
105
  status.attach_to_pin_enabled ?? device.features.attachToPinEnabled;
100
106
  device.features.unlockedAttachPin =
@@ -154,7 +160,7 @@ describe('openWalletSession', () => {
154
160
  expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
155
161
  });
156
162
 
157
- test('selects the Main PIN before opening the standard wallet when passphrase is disabled', async () => {
163
+ test('opens the already-unlocked standard wallet without repeating Main PIN when passphrase is disabled', async () => {
158
164
  const typedCall = jest.fn((request: string) => {
159
165
  if (request === 'ProtocolInfoRequest') {
160
166
  return { message: { version: 2 } };
@@ -173,11 +179,7 @@ describe('openWalletSession', () => {
173
179
 
174
180
  await getProtocolV2WalletSession(device as any, { onlyMainPin: true });
175
181
 
176
- expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
177
- source: 'wallet-session-coordinator',
178
- reason: 'open-wallet',
179
- deviceOnly: true,
180
- });
182
+ expect(device.unlockDevice).not.toHaveBeenCalled();
181
183
  expect(typedCall).not.toHaveBeenCalledWith(
182
184
  'DeviceSessionAskPassphrase',
183
185
  'Success',
@@ -186,6 +188,106 @@ describe('openWalletSession', () => {
186
188
  expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', standardSessionGet);
187
189
  });
188
190
 
191
+ test('refreshes stale wallet status before accepting an only-Main-PIN session', async () => {
192
+ let attachPinSelected = true;
193
+ const typedCall = jest.fn((request: string) => {
194
+ if (request === 'ProtocolInfoRequest') {
195
+ return { message: { version: 2 } };
196
+ }
197
+ if (request === 'DeviceSessionGet') {
198
+ return {
199
+ message: {
200
+ btc_test_address: 'standard-state',
201
+ session_id: 'standard-session',
202
+ },
203
+ };
204
+ }
205
+ if (request === 'DeviceSessionAskPassphrase') {
206
+ return { message: {} };
207
+ }
208
+ throw new Error(`Unexpected request: ${request}`);
209
+ });
210
+ const device = createDevice({
211
+ passphraseProtection: false,
212
+ unlockedAttachPin: false,
213
+ typedCall,
214
+ });
215
+ device.commands.typedCall.mockImplementation((request: string, ...args: unknown[]) => {
216
+ if (request === 'DeviceStatusGet') {
217
+ return {
218
+ message: {
219
+ device_id: 'device-1',
220
+ unlocked: true,
221
+ attach_to_pin_enabled: true,
222
+ unlocked_attach_pin: attachPinSelected,
223
+ unlocked_by_attach_to_pin: attachPinSelected,
224
+ passphrase_enabled: true,
225
+ },
226
+ };
227
+ }
228
+ return typedCall(request, ...args);
229
+ });
230
+ device.unlockDevice.mockImplementation(() => {
231
+ attachPinSelected = false;
232
+ device.features.unlockedAttachPin = false;
233
+ return Promise.resolve(device.features);
234
+ });
235
+
236
+ await expect(
237
+ getProtocolV2WalletSession(device as any, { onlyMainPin: true })
238
+ ).resolves.toMatchObject({
239
+ unlockedAttachPin: false,
240
+ });
241
+
242
+ expect(device.commands.typedCall).toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
243
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
244
+ source: 'wallet-session-coordinator',
245
+ reason: 'open-wallet',
246
+ deviceOnly: true,
247
+ });
248
+ });
249
+
250
+ test('still requires Main PIN when a cached standard session resolves to another wallet', async () => {
251
+ let sessionGetCount = 0;
252
+ const typedCall = jest.fn((request: string) => {
253
+ if (request === 'ProtocolInfoRequest') {
254
+ return { message: { version: 2 } };
255
+ }
256
+ if (request === 'DeviceSessionGet') {
257
+ sessionGetCount += 1;
258
+ return {
259
+ message: {
260
+ btc_test_address:
261
+ sessionGetCount === 1 ? 'unexpected-wallet-state' : 'cached-standard-state',
262
+ session_id:
263
+ sessionGetCount === 1 ? 'unexpected-wallet-session' : 'cached-standard-session',
264
+ },
265
+ };
266
+ }
267
+ throw new Error(`Unexpected request: ${request}`);
268
+ });
269
+ const device = createDevice({ passphraseProtection: false, typedCall });
270
+ device.getStandardInternalState = jest.fn(() => ({
271
+ passphraseState: 'cached-standard-state',
272
+ sessionId: 'cached-standard-session',
273
+ }));
274
+ device.clearStandardInternalState = jest.fn();
275
+
276
+ await expect(
277
+ getProtocolV2WalletSession(device as any, { onlyMainPin: true })
278
+ ).resolves.toMatchObject({
279
+ passphraseState: 'cached-standard-state',
280
+ newSession: 'cached-standard-session',
281
+ });
282
+
283
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
284
+ source: 'wallet-session-coordinator',
285
+ reason: 'session-recovery',
286
+ deviceOnly: true,
287
+ });
288
+ expect(sessionGetCount).toBe(2);
289
+ });
290
+
189
291
  test('reuses a Main PIN selected by the current preflight when passphrase is disabled', async () => {
190
292
  const typedCall = jest.fn((request: string) => {
191
293
  if (request === 'ProtocolInfoRequest') {
@@ -230,8 +332,7 @@ describe('openWalletSession', () => {
230
332
  }
231
333
  throw new Error(`Unexpected request: ${request}`);
232
334
  });
233
- const device = createDevice({ typedCall });
234
- device.features.unlockedAttachPin = true;
335
+ const device = createDevice({ refreshedUnlockedAttachPin: true, typedCall });
235
336
 
236
337
  await getProtocolV2WalletSession(device as any, { onlyMainPin: true });
237
338
 
@@ -1120,7 +1221,7 @@ describe('openWalletSession', () => {
1120
1221
  payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'standard' },
1121
1222
  });
1122
1223
  method.init();
1123
- const device = createDevice({ typedCall });
1224
+ const device = createDevice({ refreshedUnlocked: false, typedCall });
1124
1225
  device.features.unlocked = false;
1125
1226
  device.getDeviceState = jest
1126
1227
  .fn()
@@ -1220,6 +1321,7 @@ describe('openWalletSession', () => {
1220
1321
  });
1221
1322
 
1222
1323
  test('switches from Attach PIN to Main PIN before opening the standard wallet', async () => {
1324
+ let attachPinSelected = true;
1223
1325
  const typedCall = jest
1224
1326
  .fn()
1225
1327
  .mockResolvedValueOnce({ message: { version: 2 } })
@@ -1236,6 +1338,21 @@ describe('openWalletSession', () => {
1236
1338
  method.init();
1237
1339
  const device = createDevice({ typedCall });
1238
1340
  device.features.unlockedAttachPin = true;
1341
+ device.commands.typedCall.mockImplementation((request: string, ...args: unknown[]) => {
1342
+ if (request === 'DeviceStatusGet') {
1343
+ return {
1344
+ message: {
1345
+ device_id: 'device-1',
1346
+ unlocked: true,
1347
+ attach_to_pin_enabled: true,
1348
+ unlocked_attach_pin: attachPinSelected,
1349
+ unlocked_by_attach_to_pin: attachPinSelected,
1350
+ passphrase_enabled: true,
1351
+ },
1352
+ };
1353
+ }
1354
+ return typedCall(request, ...args);
1355
+ });
1239
1356
  device.getDeviceState = jest
1240
1357
  .fn()
1241
1358
  .mockResolvedValueOnce({
@@ -1255,6 +1372,7 @@ describe('openWalletSession', () => {
1255
1372
  },
1256
1373
  });
1257
1374
  device.unlockDevice = jest.fn().mockImplementation(() => {
1375
+ attachPinSelected = false;
1258
1376
  device.features.unlockedAttachPin = false;
1259
1377
  return Promise.resolve(device.features);
1260
1378
  });
@@ -2786,7 +2786,7 @@ describe('Protocol V2 feature adapter', () => {
2786
2786
  await expect(
2787
2787
  device.checkPassphraseStateSafety('stale-hidden-state', true, false)
2788
2788
  ).resolves.toBe(true);
2789
- expect(typedCall).toHaveBeenCalledTimes(5);
2789
+ expect(typedCall).toHaveBeenCalledTimes(6);
2790
2790
  expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
2791
2791
  eventless_wallet_session: true,
2792
2792
  });
@@ -2798,7 +2798,7 @@ describe('Protocol V2 feature adapter', () => {
2798
2798
  });
2799
2799
  expect(typedCall).toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
2800
2800
  expect(typedCall.mock.calls.filter(([request]) => request === 'DeviceStatusGet')).toHaveLength(
2801
- 2
2801
+ 3
2802
2802
  );
2803
2803
  expect(typedCall).not.toHaveBeenCalledWith('DeviceSessionAskPin', 'Success', expect.anything());
2804
2804
  });
@@ -14,7 +14,9 @@ export default class RequestQueue {
14
14
  getTask(requestId: number): RequestTask | undefined;
15
15
  getAbortController(requestId: number): AbortController | undefined;
16
16
  abortRequest(requestId: number): boolean;
17
+ private isRequestForConnectId;
17
18
  abortRequestsByConnectId(connectId: string): number;
19
+ getRequestTasksIdByConnectId(connectId: string): number[];
18
20
  abortAllRequests(): number;
19
21
  getRequestTasksId(): number[];
20
22
  resolveRequest(requestId: number, response: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"RequestQueue.d.ts","sourceRoot":"","sources":["../../src/core/RequestQueue.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGpD,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACxC,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,OAAO,CAAC,YAAY,CAAkC;IAEtD,OAAO,CAAC,oBAAoB,CAAqC;IAG1D,iBAAiB,YAAa,UAAU,YAK7C;IAEK,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW;IAY3C,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAKnD,kBAAkB,CAAC,SAAS,EAAE,MAAM;IAKpC,YAAY,CAAC,SAAS,EAAE,MAAM;IAW9B,wBAAwB,CAAC,SAAS,EAAE,MAAM;IAa1C,gBAAgB;IAYhB,iBAAiB;IAKjB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG;IAS/C,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;IAS3C,WAAW,CAAC,SAAS,EAAE,MAAM;IAI7B,2BAA2B,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC;IAYxE,2BAA2B,CACtC,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,GAC1B,OAAO,CAAC,IAAI,CAAC;IAST,mBAAmB,CAAC,SAAS,EAAE,MAAM;CAM7C"}
1
+ {"version":3,"file":"RequestQueue.d.ts","sourceRoot":"","sources":["../../src/core/RequestQueue.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGpD,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACxC,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,OAAO,CAAC,YAAY,CAAkC;IAEtD,OAAO,CAAC,oBAAoB,CAAqC;IAG1D,iBAAiB,YAAa,UAAU,YAK7C;IAEK,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW;IAY3C,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAKnD,kBAAkB,CAAC,SAAS,EAAE,MAAM;IAKpC,YAAY,CAAC,SAAS,EAAE,MAAM;IAUrC,OAAO,CAAC,qBAAqB;IAUtB,wBAAwB,CAAC,SAAS,EAAE,MAAM;IAY1C,4BAA4B,CAAC,SAAS,EAAE,MAAM;IAO9C,gBAAgB;IAYhB,iBAAiB;IAKjB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG;IAS/C,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;IAS3C,WAAW,CAAC,SAAS,EAAE,MAAM;IAI7B,2BAA2B,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC;IAYxE,2BAA2B,CACtC,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,GAC1B,OAAO,CAAC,IAAI,CAAC;IAST,mBAAmB,CAAC,SAAS,EAAE,MAAM;CAM7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";AACA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAClC,OAAO,EAEL,KAAK,6BAA6B,EAElC,KAAK,YAAY,EAIlB,MAAM,wBAAwB,CAAC;AA+BhC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAsB1C,OAAO,eAAe,MAAM,2BAA2B,CAAC;AAYxD,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,UAAU,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAmD,MAAM,WAAW,CAAC;AAI9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWpD,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;AA0E7D,eAAO,MAAM,OAAO,YAAmB,WAAW,WAAW,WAAW,iBAoFvE,CAAC;AAyqBF,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAWpF;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAW/E;AAED,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAQlF;AAED,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAMrF;AA6JD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,CAMtF;AAwLD,eAAO,MAAM,MAAM,YAAa,WAAW,cAAc,MAAM,SA0G9D,CAAC;AAuGF,eAAO,MAAM,qBAAqB,gFAejC,CAAC;AAiLF,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,YAAY;IAC5C,OAAO,CAAC,cAAc,CAAoB;IAE1C,SAAgB,aAAa,EAAE,MAAM,CAAC;IAEtC,OAAO,CAAC,YAAY,CAAsB;IAE1C,OAAO,CAAC,cAAc,CAAC,CAAgB;IAGvC,OAAO,CAAC,sBAAsB,CAAoC;IAElE,OAAO,CAAC,iBAAiB,CAAoB;;IAS7C,OAAO,CAAC,cAAc;IA6BhB,aAAa,CAAC,OAAO,EAAE,WAAW;IAuExC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAOV,gBAAgB;CAiC/B;AAED,eAAO,MAAM,QAAQ,YAIpB,CAAC;AAEF,eAAO,MAAM,aAAa,uBAYzB,CAAC;AAMF,eAAO,MAAM,IAAI,aACL,eAAe,aACd,GAAG,WACL,6BAA6B,8BAiBvC,CAAC;AAEF,eAAO,MAAM,eAAe;SAKrB,eAAe,CAAC,KAAK,CAAC;eAChB,GAAG;;UASf,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";AACA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAClC,OAAO,EAEL,KAAK,6BAA6B,EAElC,KAAK,YAAY,EAIlB,MAAM,wBAAwB,CAAC;AA+BhC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAsB1C,OAAO,eAAe,MAAM,2BAA2B,CAAC;AAYxD,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,UAAU,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAmD,MAAM,WAAW,CAAC;AAI9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWpD,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;AA0E7D,eAAO,MAAM,OAAO,YAAmB,WAAW,WAAW,WAAW,iBAoFvE,CAAC;AAyqBF,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAWpF;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAW/E;AAED,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAQlF;AAED,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAMrF;AA6JD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,CAMtF;AAwLD,eAAO,MAAM,MAAM,YAAa,WAAW,cAAc,MAAM,SA2G9D,CAAC;AA8GF,eAAO,MAAM,qBAAqB,gFAejC,CAAC;AAiLF,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,YAAY;IAC5C,OAAO,CAAC,cAAc,CAAoB;IAE1C,SAAgB,aAAa,EAAE,MAAM,CAAC;IAEtC,OAAO,CAAC,YAAY,CAAsB;IAE1C,OAAO,CAAC,cAAc,CAAC,CAAgB;IAGvC,OAAO,CAAC,sBAAsB,CAAoC;IAElE,OAAO,CAAC,iBAAiB,CAAoB;;IAS7C,OAAO,CAAC,cAAc;IA6BhB,aAAa,CAAC,OAAO,EAAE,WAAW;IAuExC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAOV,gBAAgB;CAiC/B;AAED,eAAO,MAAM,QAAQ,YAIpB,CAAC;AAEF,eAAO,MAAM,aAAa,uBAYzB,CAAC;AAMF,eAAO,MAAM,IAAI,aACL,eAAe,aACd,GAAG,WACL,6BAA6B,8BAiBvC,CAAC;AAEF,eAAO,MAAM,eAAe;SAKrB,eAAe,CAAC,KAAK,CAAC;eAChB,GAAG;;UASf,CAAC"}
package/dist/index.js CHANGED
@@ -41909,6 +41909,10 @@ function getProtocolV2WalletSession(device, options) {
41909
41909
  const markWalletStatusRefreshed = () => {
41910
41910
  walletStatusRefreshed = true;
41911
41911
  };
41912
+ if ((options === null || options === void 0 ? void 0 : options.onlyMainPin) && options.mainPinSelected !== true) {
41913
+ yield refreshProtocolV2DeviceStatus(device);
41914
+ markWalletStatusRefreshed();
41915
+ }
41912
41916
  let mainPinAuthenticated = (options === null || options === void 0 ? void 0 : options.mainPinSelected) === true ||
41913
41917
  ((options === null || options === void 0 ? void 0 : options.onlyMainPin) === true &&
41914
41918
  ((_d = device.features) === null || _d === void 0 ? void 0 : _d.unlocked) === true &&
@@ -41981,7 +41985,7 @@ function getProtocolV2WalletSession(device, options) {
41981
41985
  standardWalletSelected = true;
41982
41986
  }
41983
41987
  });
41984
- const selectStandardWallet = () => __awaiter(this, void 0, void 0, function* () {
41988
+ const selectStandardWallet = (forceMainPin = false) => __awaiter(this, void 0, void 0, function* () {
41985
41989
  var _q;
41986
41990
  if (((_q = device.features) === null || _q === void 0 ? void 0 : _q.passphraseProtection) === true) {
41987
41991
  yield selectMainPin();
@@ -41992,7 +41996,8 @@ function getProtocolV2WalletSession(device, options) {
41992
41996
  standardWalletSelected = true;
41993
41997
  }
41994
41998
  else if (!standardWalletSelected) {
41995
- yield selectMainPin(true);
41999
+ yield selectMainPin(forceMainPin);
42000
+ standardWalletSelected = true;
41996
42001
  }
41997
42002
  });
41998
42003
  if (readCurrentAttachPinSession) {
@@ -42091,7 +42096,7 @@ function getProtocolV2WalletSession(device, options) {
42091
42096
  }
42092
42097
  if (options === null || options === void 0 ? void 0 : options.onlyMainPin) {
42093
42098
  (_h = device.clearStandardInternalState) === null || _h === void 0 ? void 0 : _h.call(device);
42094
- yield selectStandardWallet();
42099
+ yield selectStandardWallet(true);
42095
42100
  response = yield getDeviceSession(device, sessionGetRequest());
42096
42101
  }
42097
42102
  else {
@@ -65590,10 +65595,17 @@ class RequestQueue {
65590
65595
  }
65591
65596
  return false;
65592
65597
  }
65598
+ isRequestForConnectId(request, connectId) {
65599
+ var _a, _b;
65600
+ const { method } = request;
65601
+ return (method.connectId === connectId ||
65602
+ ((_a = method.device) === null || _a === void 0 ? void 0 : _a.mainId) === connectId ||
65603
+ ((_b = method.device) === null || _b === void 0 ? void 0 : _b.getConnectId()) === connectId);
65604
+ }
65593
65605
  abortRequestsByConnectId(connectId) {
65594
65606
  let count = 0;
65595
65607
  this.requestQueue.forEach((request, _) => {
65596
- if (request.abortController && request.method.connectId === connectId) {
65608
+ if (request.abortController && this.isRequestForConnectId(request, connectId)) {
65597
65609
  request.abortController.abort();
65598
65610
  request.abortController = undefined;
65599
65611
  count++;
@@ -65601,6 +65613,11 @@ class RequestQueue {
65601
65613
  });
65602
65614
  return count;
65603
65615
  }
65616
+ getRequestTasksIdByConnectId(connectId) {
65617
+ return Array.from(this.requestQueue.values())
65618
+ .filter(request => this.isRequestForConnectId(request, connectId))
65619
+ .map(request => request.id);
65620
+ }
65604
65621
  abortAllRequests() {
65605
65622
  let count = 0;
65606
65623
  this.requestQueue.forEach(request => {
@@ -66643,14 +66660,14 @@ const ensureConnected = (_context, method, connectId, pollingId, abortSignal) =>
66643
66660
  return poll();
66644
66661
  });
66645
66662
  const cancel = (context, connectId) => {
66646
- var _a, _b, _c;
66663
+ var _a, _b;
66647
66664
  const { requestQueue, setPrePendingCallPromise } = context;
66648
66665
  if (connectId) {
66649
66666
  try {
66650
66667
  requestQueue.cancelCallbackTasks(connectId);
66651
- const requestIds = requestQueue.getRequestTasksId();
66668
+ const requestIds = requestQueue.getRequestTasksIdByConnectId(connectId);
66652
66669
  Log.debug(`Cancel Api connect requestQueues: length:${requestIds.length} requestIds:${requestIds.join(',')}`);
66653
- requestQueue.abortAllRequests();
66670
+ requestQueue.abortRequestsByConnectId(connectId);
66654
66671
  const canceledDevices = [];
66655
66672
  const interruptDevice = (device, deviceConnectId) => {
66656
66673
  if (!device || canceledDevices.includes(device)) {
@@ -66663,7 +66680,7 @@ const cancel = (context, connectId) => {
66663
66680
  const task = requestQueue.getTask(requestId);
66664
66681
  Log.debug('Cancel Api connect task: ', task);
66665
66682
  if (task) {
66666
- interruptDevice((_a = task.method) === null || _a === void 0 ? void 0 : _a.device, (_b = task.method.connectId) !== null && _b !== void 0 ? _b : connectId);
66683
+ interruptDevice((_a = task.method) === null || _a === void 0 ? void 0 : _a.device, connectId);
66667
66684
  interruptDevice(deviceCacheMap.get(connectId), connectId);
66668
66685
  requestQueue.rejectRequest(requestId, hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled));
66669
66686
  }
@@ -66692,7 +66709,7 @@ const cancel = (context, connectId) => {
66692
66709
  const task = requestQueue.getTask(requestId);
66693
66710
  Log.debug('Cancel Api connect task: ', task);
66694
66711
  if (task) {
66695
- interruptDevice((_c = task.method) === null || _c === void 0 ? void 0 : _c.device);
66712
+ interruptDevice((_b = task.method) === null || _b === void 0 ? void 0 : _b.device);
66696
66713
  if (task.method.connectId) {
66697
66714
  interruptDevice(deviceCacheMap.get(task.method.connectId));
66698
66715
  pollingManager.stop(task.method.connectId);
@@ -66715,8 +66732,10 @@ const cancel = (context, connectId) => {
66715
66732
  });
66716
66733
  }
66717
66734
  }
66718
- cleanup();
66719
- closePopup();
66735
+ cleanup(connectId);
66736
+ if (!connectId || _uiPromises.length === 0) {
66737
+ closePopup();
66738
+ }
66720
66739
  };
66721
66740
  const checkPassphraseEnableState = (method, features) => {
66722
66741
  if (!method.useDevicePassphraseState)
@@ -66746,9 +66765,13 @@ const shouldCheckPassphraseState = (method, device) => {
66746
66765
  }
66747
66766
  return device.hasUsePassphrase();
66748
66767
  };
66749
- const cleanup = () => {
66750
- const pendingUiPromises = _uiPromises;
66751
- _uiPromises = [];
66768
+ const cleanup = (connectId) => {
66769
+ const pendingUiPromises = connectId
66770
+ ? _uiPromises.filter(uiPromise => { var _a, _b; return ((_a = uiPromise.data) === null || _a === void 0 ? void 0 : _a.mainId) === connectId || ((_b = uiPromise.data) === null || _b === void 0 ? void 0 : _b.getConnectId()) === connectId; })
66771
+ : _uiPromises;
66772
+ _uiPromises = connectId
66773
+ ? _uiPromises.filter(uiPromise => !pendingUiPromises.includes(uiPromise))
66774
+ : [];
66752
66775
  rejectUiPromises(pendingUiPromises, hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.ActionCancelled, 'UI request was cancelled'));
66753
66776
  };
66754
66777
  const removeDeviceListener = (device) => {
@@ -1 +1 @@
1
- {"version":3,"file":"walletSession.d.ts","sourceRoot":"","sources":["../../../src/protocols/protocol-v2/walletSession.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAuBlD,wBAAsB,6BAA6B,CAAC,MAAM,EAAE,MAAM,0DAGjE;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE,MAAM,qCAGjE;AAED,wBAAsB,qCAAqC,CAAC,MAAM,EAAE,MAAM,oBAgBzE;AA8JD,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,2BAA2B,CAAC,EAAE,OAAO,CAAC;IAEtC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;;;;;;GAuXF;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,MAAM,EACd,uBAAuB,EAAE,MAAM,EAC/B,aAAa,CAAC,EAAE,OAAO;;;;;;GAOxB"}
1
+ {"version":3,"file":"walletSession.d.ts","sourceRoot":"","sources":["../../../src/protocols/protocol-v2/walletSession.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAuBlD,wBAAsB,6BAA6B,CAAC,MAAM,EAAE,MAAM,0DAGjE;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE,MAAM,qCAGjE;AAED,wBAAsB,qCAAqC,CAAC,MAAM,EAAE,MAAM,oBAgBzE;AA8JD,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,2BAA2B,CAAC,EAAE,OAAO,CAAC;IAEtC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;;;;;;GA6XF;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,MAAM,EACd,uBAAuB,EAAE,MAAM,EAC/B,aAAa,CAAC,EAAE,OAAO;;;;;;GAOxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-core",
3
- "version": "1.2.2-alpha.7",
3
+ "version": "1.2.2-alpha.9",
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.2-alpha.7",
29
- "@onekeyfe/hd-transport": "1.2.2-alpha.7",
28
+ "@onekeyfe/hd-shared": "1.2.2-alpha.9",
29
+ "@onekeyfe/hd-transport": "1.2.2-alpha.9",
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": "29b87f1c04fea60459d900ef1f61cb9f17eaa4d3"
49
+ "gitHead": "c097e98f32c451ccdda468da1195daf679e991e6"
50
50
  }
@@ -56,11 +56,20 @@ export default class RequestQueue {
56
56
  return false;
57
57
  }
58
58
 
59
+ private isRequestForConnectId(request: RequestTask, connectId: string) {
60
+ const { method } = request;
61
+ return (
62
+ method.connectId === connectId ||
63
+ method.device?.mainId === connectId ||
64
+ method.device?.getConnectId() === connectId
65
+ );
66
+ }
67
+
59
68
  // 取消与指定connectId相关的所有请求
60
69
  public abortRequestsByConnectId(connectId: string) {
61
70
  let count = 0;
62
71
  this.requestQueue.forEach((request, _) => {
63
- if (request.abortController && request.method.connectId === connectId) {
72
+ if (request.abortController && this.isRequestForConnectId(request, connectId)) {
64
73
  request.abortController.abort();
65
74
  request.abortController = undefined;
66
75
  count++;
@@ -69,6 +78,12 @@ export default class RequestQueue {
69
78
  return count;
70
79
  }
71
80
 
81
+ public getRequestTasksIdByConnectId(connectId: string) {
82
+ return Array.from(this.requestQueue.values())
83
+ .filter(request => this.isRequestForConnectId(request, connectId))
84
+ .map(request => request.id);
85
+ }
86
+
72
87
  // 取消所有请求
73
88
  public abortAllRequests() {
74
89
  let count = 0;
package/src/core/index.ts CHANGED
@@ -1333,7 +1333,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1333
1333
  // cancel callback tasks
1334
1334
  requestQueue.cancelCallbackTasks(connectId);
1335
1335
 
1336
- const requestIds = requestQueue.getRequestTasksId();
1336
+ const requestIds = requestQueue.getRequestTasksIdByConnectId(connectId);
1337
1337
  Log.debug(
1338
1338
  `Cancel Api connect requestQueues: length:${requestIds.length} requestIds:${requestIds.join(
1339
1339
  ','
@@ -1341,10 +1341,9 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1341
1341
  );
1342
1342
  // Abort before rejecting: rejectRequest releases the task and would make
1343
1343
  // its AbortController unreachable to an in-flight method loop.
1344
- // This branch rejects every queued request below. Abort the same set first so
1345
- // methods whose physical connectId is selected internally (for example
1346
- // Desktop WebUSB firmwareUpdateV4) cannot keep retrying after rejection.
1347
- requestQueue.abortAllRequests();
1344
+ // Match both the requested connectId and a device selected internally by the
1345
+ // method, such as Desktop WebUSB firmwareUpdateV4.
1346
+ requestQueue.abortRequestsByConnectId(connectId);
1348
1347
  const canceledDevices: Device[] = [];
1349
1348
  const interruptDevice = (device: Device | undefined, deviceConnectId: string) => {
1350
1349
  if (!device || canceledDevices.includes(device)) {
@@ -1360,7 +1359,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1360
1359
  // During ensureConnected the method has a connectId but device is
1361
1360
  // assigned only after the poll succeeds. Interrupt the cached BLE
1362
1361
  // Device so an in-flight acquire/initialize cannot finish.
1363
- interruptDevice(task.method?.device, task.method.connectId ?? connectId);
1362
+ interruptDevice(task.method?.device, connectId);
1364
1363
  interruptDevice(deviceCacheMap.get(connectId), connectId);
1365
1364
  requestQueue.rejectRequest(
1366
1365
  requestId,
@@ -1421,8 +1420,10 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1421
1420
  }
1422
1421
  }
1423
1422
 
1424
- cleanup();
1425
- closePopup();
1423
+ cleanup(connectId);
1424
+ if (!connectId || _uiPromises.length === 0) {
1425
+ closePopup();
1426
+ }
1426
1427
  };
1427
1428
 
1428
1429
  const checkPassphraseEnableState = (method: BaseMethod, features?: Features) => {
@@ -1460,9 +1461,16 @@ const shouldCheckPassphraseState = (method: BaseMethod, device: Device) => {
1460
1461
  return device.hasUsePassphrase();
1461
1462
  };
1462
1463
 
1463
- const cleanup = () => {
1464
- const pendingUiPromises = _uiPromises;
1465
- _uiPromises = [];
1464
+ const cleanup = (connectId?: string) => {
1465
+ const pendingUiPromises = connectId
1466
+ ? _uiPromises.filter(
1467
+ uiPromise =>
1468
+ uiPromise.data?.mainId === connectId || uiPromise.data?.getConnectId() === connectId
1469
+ )
1470
+ : _uiPromises;
1471
+ _uiPromises = connectId
1472
+ ? _uiPromises.filter(uiPromise => !pendingUiPromises.includes(uiPromise))
1473
+ : [];
1466
1474
  rejectUiPromises(
1467
1475
  pendingUiPromises,
1468
1476
  ERRORS.TypedError(HardwareErrorCode.ActionCancelled, 'UI request was cancelled')
@@ -273,6 +273,10 @@ export async function getProtocolV2WalletSession(
273
273
  const markWalletStatusRefreshed = () => {
274
274
  walletStatusRefreshed = true;
275
275
  };
276
+ if (options?.onlyMainPin && options.mainPinSelected !== true) {
277
+ await refreshProtocolV2DeviceStatus(device);
278
+ markWalletStatusRefreshed();
279
+ }
276
280
  let mainPinAuthenticated =
277
281
  options?.mainPinSelected === true ||
278
282
  (options?.onlyMainPin === true &&
@@ -371,7 +375,7 @@ export async function getProtocolV2WalletSession(
371
375
  }
372
376
  };
373
377
 
374
- const selectStandardWallet = async () => {
378
+ const selectStandardWallet = async (forceMainPin = false) => {
375
379
  if (device.features?.passphraseProtection === true) {
376
380
  // Main PIN authenticates the device; an empty host passphrase selects the standard derivation.
377
381
  await selectMainPin();
@@ -387,8 +391,10 @@ export async function getProtocolV2WalletSession(
387
391
  standardWalletSelected = true;
388
392
  } else if (!standardWalletSelected) {
389
393
  // Without passphrase protection there is no empty-passphrase selector.
390
- // Main PIN selection is the only authoritative switch back to the standard wallet.
391
- await selectMainPin(true);
394
+ // An unlocked non-Attach-PIN device is already in the only available wallet context.
395
+ // Force Main PIN only when recovering from a mismatched cached standard session.
396
+ await selectMainPin(forceMainPin);
397
+ standardWalletSelected = true;
392
398
  }
393
399
  };
394
400
 
@@ -499,7 +505,7 @@ export async function getProtocolV2WalletSession(
499
505
  }
500
506
  if (options?.onlyMainPin) {
501
507
  device.clearStandardInternalState?.();
502
- await selectStandardWallet();
508
+ await selectStandardWallet(true);
503
509
  response = await getDeviceSession(device, sessionGetRequest());
504
510
  } else {
505
511
  device.clearInternalState();