@trezor/connect 9.6.0 → 9.6.1-beta.2

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 (87) hide show
  1. package/CHANGELOG.md +37 -8
  2. package/README.md +1 -1
  3. package/lib/api/blockchainEstimateFee.js +1 -0
  4. package/lib/api/discoverAccounts.js +10 -2
  5. package/lib/api/eos/eosSignTx.d.ts +3 -3
  6. package/lib/api/ethereum/api/ethereumSignMessage.js +1 -1
  7. package/lib/api/ethereum/api/ethereumSignTypedData.d.ts +1 -1
  8. package/lib/api/firmware/getBinary.js +2 -2
  9. package/lib/api/getAddress.d.ts +1 -0
  10. package/lib/api/getAddress.js +1 -0
  11. package/lib/api/getCoinInfo.d.ts +3 -3
  12. package/lib/api/getNonce.d.ts +9 -0
  13. package/lib/api/getNonce.js +15 -0
  14. package/lib/api/index.d.ts +1 -0
  15. package/lib/api/index.js +4 -2
  16. package/lib/api/nem/nemSignTx.d.ts +1 -1
  17. package/lib/api/resetDevice.d.ts +2 -3
  18. package/lib/api/resetDevice.js +47 -49
  19. package/lib/api/signMessage.js +1 -1
  20. package/lib/api/signTransaction.js +6 -1
  21. package/lib/backend/Blockchain.d.ts +1 -0
  22. package/lib/backend/fees/BitcoinFeeLevels.d.ts +2 -4
  23. package/lib/backend/fees/BitcoinFeeLevels.js +11 -73
  24. package/lib/backend/fees/EthereumFeeLevels.d.ts +1 -3
  25. package/lib/backend/fees/EthereumFeeLevels.js +8 -5
  26. package/lib/backend/fees/MiscFeeLevels.d.ts +0 -3
  27. package/lib/backend/fees/MiscFeeLevels.js +2 -2
  28. package/lib/constants/errors.d.ts +1 -0
  29. package/lib/constants/errors.js +1 -0
  30. package/lib/core/AbstractMethod.d.ts +1 -1
  31. package/lib/core/index.js +43 -53
  32. package/lib/core/onCallFirmwareUpdate.js +2 -2
  33. package/lib/data/coinInfo.d.ts +11 -11
  34. package/lib/data/coinInfo.js +1 -1
  35. package/lib/data/connectSettings.js +0 -3
  36. package/lib/data/defaultFeeLevels.d.ts +3 -1
  37. package/lib/data/defaultFeeLevels.js +22 -12
  38. package/lib/data/deviceAuthenticityConfig.js +4 -1
  39. package/lib/data/firmwareInfo.d.ts +1 -0
  40. package/lib/data/firmwareInfo.js +14 -1
  41. package/lib/data/version.d.ts +1 -1
  42. package/lib/data/version.js +1 -1
  43. package/lib/device/Device.d.ts +20 -21
  44. package/lib/device/Device.js +78 -123
  45. package/lib/device/DeviceCurrentSession.d.ts +441 -4027
  46. package/lib/device/DeviceCurrentSession.js +75 -44
  47. package/lib/device/DeviceList.d.ts +3 -8
  48. package/lib/device/DeviceList.js +21 -77
  49. package/lib/device/TransportManager.d.ts +2 -6
  50. package/lib/device/TransportManager.js +11 -7
  51. package/lib/device/checkFirmwareRevision.js +2 -2
  52. package/lib/device/workflow/index.d.ts +2 -0
  53. package/lib/device/workflow/index.js +6 -0
  54. package/lib/device/workflow/validateState.d.ts +3 -0
  55. package/lib/device/workflow/validateState.js +97 -0
  56. package/lib/events/device.d.ts +19 -1
  57. package/lib/events/device.js +2 -0
  58. package/lib/events/index.d.ts +4 -0
  59. package/lib/events/ui-request.d.ts +24 -3
  60. package/lib/events/ui-request.js +3 -0
  61. package/lib/events/ui-response.d.ts +11 -1
  62. package/lib/events/ui-response.js +1 -0
  63. package/lib/factory.js +1 -0
  64. package/lib/tsconfig.lib.tsbuildinfo +1 -1
  65. package/lib/types/api/applySettings.d.ts +1 -0
  66. package/lib/types/api/discoverAccounts.d.ts +17 -4
  67. package/lib/types/api/discoverAccounts.js +2 -0
  68. package/lib/types/api/getNonce.d.ts +4 -0
  69. package/lib/types/api/getNonce.js +3 -0
  70. package/lib/types/api/index.d.ts +2 -0
  71. package/lib/types/coinInfo.d.ts +6 -6
  72. package/lib/types/coinInfo.js +2 -0
  73. package/lib/types/device.d.ts +3 -4
  74. package/lib/types/fees.d.ts +1 -1
  75. package/lib/types/fees.js +1 -1
  76. package/lib/types/params.d.ts +1 -0
  77. package/lib/types/settings.d.ts +0 -1
  78. package/lib/types/workflow.d.ts +8 -0
  79. package/lib/types/workflow.js +3 -0
  80. package/lib/utils/formatUtils.d.ts +1 -0
  81. package/lib/utils/formatUtils.js +9 -1
  82. package/lib/utils/uiPromiseManager.d.ts +2 -0
  83. package/package.json +17 -17
  84. package/lib/data/downloadReleasesMetadata.d.ts +0 -7
  85. package/lib/data/downloadReleasesMetadata.js +0 -18
  86. package/lib/data/models.d.ts +0 -8
  87. package/lib/data/models.js +0 -49
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getRelease = exports.getReleaseInfo = exports.getFirmwareStatus = exports.getInfo = exports.getReleases = exports.parseFirmwareReleases = void 0;
3
+ exports.getRelease = exports.getReleaseInfo = exports.getFirmwareStatus = exports.getInfo = exports.getOnlineReleases = exports.getReleases = exports.parseFirmwareReleases = void 0;
4
4
  const device_utils_1 = require("@trezor/device-utils");
5
5
  const utils_1 = require("@trezor/utils");
6
+ const assets_1 = require("../utils/assets");
6
7
  const firmwareUtils_1 = require("../utils/firmwareUtils");
7
8
  const releases = Object.values(device_utils_1.DeviceModelInternal).reduce((acc, key) => ({ ...acc, [key]: [] }), {});
8
9
  const parseFirmwareReleases = (json, deviceModel) => {
@@ -16,6 +17,18 @@ const parseFirmwareReleases = (json, deviceModel) => {
16
17
  exports.parseFirmwareReleases = parseFirmwareReleases;
17
18
  const getReleases = (deviceModel) => releases[deviceModel] || [];
18
19
  exports.getReleases = getReleases;
20
+ const getOnlineReleases = async (internalModel) => {
21
+ const url = `https://data.trezor.io/firmware/${internalModel.toLowerCase()}/releases.json`;
22
+ const response = await (0, assets_1.httpRequest)(url, 'json', {
23
+ signal: AbortSignal.timeout(10000),
24
+ skipLocalForceDownload: true,
25
+ });
26
+ if ((0, firmwareUtils_1.isValidReleases)(response)) {
27
+ return response;
28
+ }
29
+ return [];
30
+ };
31
+ exports.getOnlineReleases = getOnlineReleases;
19
32
  const getChangelog = (releases2, features) => {
20
33
  if (features.bootloader_mode) {
21
34
  if (features.firmware_present && features.major_version === 1) {
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "9.6.0";
1
+ export declare const VERSION = "9.6.1-beta.2";
2
2
  export declare const DEFAULT_DOMAIN: string;
3
3
  export declare const CONTENT_SCRIPT_VERSION = 1;
4
4
  export declare const DEEPLINK_VERSION = 1;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DEEPLINK_VERSION = exports.CONTENT_SCRIPT_VERSION = exports.DEFAULT_DOMAIN = exports.VERSION = void 0;
4
- exports.VERSION = '9.6.0';
4
+ exports.VERSION = '9.6.1-beta.2';
5
5
  const versionN = exports.VERSION.split('.').map(s => parseInt(s, 10));
6
6
  const isBeta = exports.VERSION.includes('beta');
7
7
  exports.DEFAULT_DOMAIN = isBeta
@@ -1,11 +1,11 @@
1
- import { TransportProtocol } from '@trezor/protocol';
1
+ import { TransportProtocol, thp as protocolThp } from '@trezor/protocol';
2
2
  import { Session } from '@trezor/transport';
3
3
  import { type Descriptor, type Transport } from '@trezor/transport';
4
4
  import { TypedEmitter } from '@trezor/utils';
5
5
  import { PROTO } from '../constants';
6
6
  import { TypedCallProvider } from './DeviceCurrentSession';
7
7
  import { IStateStorage } from './StateStorage';
8
- import { DEVICE, DeviceButtonRequestPayload, DeviceVersionChanged, UiResponsePassphrase, UiResponsePin, UiResponseWord } from '../events';
8
+ import { DEVICE, DeviceButtonRequestPayload, DeviceThpCredentialsChangedPayload, DeviceThpPairingPayload, DeviceVersionChanged, UiResponsePassphrase, UiResponsePin, UiResponseThpPairingTag, UiResponseWord } from '../events';
9
9
  import { DeviceFirmwareStatus, DeviceState, Device as DeviceTyped, DeviceUniquePath, FirmwareType, ReleaseInfo, UnavailableCapabilities, VersionArray } from '../types';
10
10
  type RunOptions = {
11
11
  skipFinalReload?: boolean;
@@ -42,6 +42,11 @@ export interface DeviceEvents {
42
42
  payload: DeviceButtonRequestPayload;
43
43
  };
44
44
  [DEVICE.FIRMWARE_VERSION_CHANGED]: DeviceVersionChanged['payload'];
45
+ [DEVICE.THP_PAIRING]: {
46
+ payload: DeviceThpPairingPayload;
47
+ callback: (response: Result<UiResponseThpPairingTag['payload']>) => void;
48
+ };
49
+ [DEVICE.THP_CREDENTIALS_CHANGED]: DeviceThpCredentialsChangedPayload;
45
50
  }
46
51
  type DeviceLifecycle = typeof DEVICE.CONNECT | typeof DEVICE.CONNECT_UNACQUIRED | typeof DEVICE.DISCONNECT | typeof DEVICE.CHANGED;
47
52
  type DeviceLifecycleListener = (lifecycle: DeviceLifecycle) => void;
@@ -53,16 +58,16 @@ type DeviceParams = {
53
58
  };
54
59
  export declare class Device extends TypedEmitter<DeviceEvents> {
55
60
  readonly transport: Transport;
56
- readonly protocol: TransportProtocol;
57
61
  readonly transportPath: import("@trezor/transport/lib/types").PathPublic;
58
62
  readonly bluetoothProps: {
59
63
  id: string;
60
64
  } | undefined;
61
65
  private thp;
62
- private readonly transportDescriptorType;
63
- private session;
64
- private transportSessionOwner;
65
- private lastAcquiredHere;
66
+ private readonly possibleHIDdevice;
67
+ private sessionAcquired;
68
+ private _protocol;
69
+ get protocol(): TransportProtocol;
70
+ getThpState(): protocolThp.ThpState | undefined;
66
71
  private unreadableError?;
67
72
  private _firmwareStatus;
68
73
  get firmwareStatus(): DeviceFirmwareStatus;
@@ -123,14 +128,13 @@ export declare class Device extends TypedEmitter<DeviceEvents> {
123
128
  experimental_features: boolean | null;
124
129
  internal_model: PROTO.DeviceModelInternal;
125
130
  };
126
- private _featuresNeedsReload;
131
+ private wasUsedElsewhere;
127
132
  private acquirePromise?;
128
133
  private releasePromise?;
129
134
  private runAbort?;
130
135
  private runPromise?;
131
136
  private keepTransportSession;
132
137
  private currentSession?;
133
- private inconsistent;
134
138
  private instance;
135
139
  private state;
136
140
  private stateStorage?;
@@ -147,20 +151,18 @@ export declare class Device extends TypedEmitter<DeviceEvents> {
147
151
  private sessionDfd?;
148
152
  handshakeFinished: boolean;
149
153
  constructor({ id, transport, descriptor, listener }: DeviceParams);
154
+ private readonly onTransportStopped;
155
+ private readonly onTransportDeviceEvent;
150
156
  private getSessionChangePromise;
151
157
  private waitAndCompareSession;
152
158
  acquire(): import("@trezor/transport/lib/types").AsyncResultWithTypedError<Session, "Network request failed" | "Wrong result type." | "device disconnected during action" | "unexpected error" | "Aborted by timeout" | "Aborted by signal" | "This transport can not be used in this environment" | "device not found" | "Unable to open device" | "wrong previous session" | "LIBUSB_ERROR_ACCESS">;
153
- release(): Promise<import("@trezor/transport/lib/types").Success<null> | {
154
- success: false;
155
- error: "Network request failed" | "Wrong result type." | "device disconnected during action" | "unexpected error" | "session not found" | "Aborted by timeout" | "Aborted by signal" | "This transport can not be used in this environment" | "device not found" | "Unable to open device" | "wrong previous session";
156
- message?: string;
157
- } | undefined>;
159
+ release(): import("@trezor/transport/lib/types").AsyncResultWithTypedError<null, "Network request failed" | "Wrong result type." | "device disconnected during action" | "unexpected error" | "session not found" | "Aborted by timeout" | "Aborted by signal" | "This transport can not be used in this environment" | "device not found" | "Unable to open device" | "wrong previous session"> | undefined;
158
160
  handshake(delay?: number): Promise<void>;
159
- updateDescriptor(descriptor: Descriptor): Promise<void>;
161
+ private updateDescriptor;
160
162
  run(fn?: () => Promise<void>, options?: RunOptions): Promise<void>;
161
163
  interrupt(reason: Error): Promise<void>;
162
164
  get currentRun(): Promise<void> | undefined;
163
- usedElsewhere(): void;
165
+ private usedElsewhere;
164
166
  private _runInner;
165
167
  getCurrentSession(): TypedCallProvider;
166
168
  getCommands(): {
@@ -228,7 +230,6 @@ export declare class Device extends TypedEmitter<DeviceEvents> {
228
230
  getInstance(): number;
229
231
  getState(): DeviceState | undefined;
230
232
  setState(state?: Partial<DeviceState>): void;
231
- validateState(preauthorized?: boolean): Promise<`${string}@${string}:${number}` | undefined>;
232
233
  initialize(useCardanoDerivation: boolean): Promise<void>;
233
234
  initStorage(storage: IStateStorage): void;
234
235
  getFeatures(): Promise<void>;
@@ -247,14 +248,13 @@ export declare class Device extends TypedEmitter<DeviceEvents> {
247
248
  }>;
248
249
  private _uploadTranslationData;
249
250
  private _updateFeatures;
250
- prompt<T extends typeof DEVICE.PIN | typeof DEVICE.PASSPHRASE | typeof DEVICE.WORD>(type: T, args: Omit<DeviceEvents[T], 'callback'>): Promise<Parameters<DeviceEvents[T]["callback"]>[0]>;
251
+ prompt<T extends typeof DEVICE.PIN | typeof DEVICE.PASSPHRASE | typeof DEVICE.WORD | typeof DEVICE.THP_PAIRING>(type: T, args: Omit<DeviceEvents[T], 'callback'>): Promise<Parameters<DeviceEvents[T]["callback"]>[0]>;
251
252
  isUnacquired(): boolean;
252
253
  isUnreadable(): boolean;
253
- disconnect(): Promise<void>;
254
+ private disconnect;
254
255
  isBootloader(): boolean;
255
256
  isInitialized(): boolean;
256
257
  isSeedless(): boolean;
257
- isInconsistent(): boolean;
258
258
  getVersion(): VersionArray | undefined;
259
259
  atLeast(versions: string[] | string): boolean;
260
260
  isUsed(): boolean;
@@ -263,7 +263,6 @@ export declare class Device extends TypedEmitter<DeviceEvents> {
263
263
  getUniquePath(): DeviceUniquePath;
264
264
  isT1(): boolean;
265
265
  hasUnexpectedMode(allow: string[], require: string[]): "ui-device_bootloader_mode" | "ui-device_not_in_bootloader_mode" | "ui-device_not_initialized" | "ui-device_seedless" | null;
266
- dispose(): void;
267
266
  private getMode;
268
267
  toMessageObject(): DeviceTyped;
269
268
  }
@@ -15,26 +15,28 @@ const DataManager_1 = require("../data/DataManager");
15
15
  const coinInfo_1 = require("../data/coinInfo");
16
16
  const firmwareInfo_1 = require("../data/firmwareInfo");
17
17
  const getLanguage_1 = require("../data/getLanguage");
18
- const models_1 = require("../data/models");
19
18
  const events_1 = require("../events");
20
19
  const types_1 = require("../types");
21
20
  const debug_1 = require("../utils/debug");
22
21
  const deviceFeaturesUtils_1 = require("../utils/deviceFeaturesUtils");
23
- const pathUtils_1 = require("../utils/pathUtils");
24
22
  const _log = (0, debug_1.initLog)('Device');
25
23
  exports.CANCEL_TIMEOUT = 1_000;
26
24
  exports.GET_FEATURES_TIMEOUT = 3_000;
27
25
  exports.GET_FEATURES_TIMEOUT_REACT_NATIVE = 20_000;
28
26
  class Device extends utils_1.TypedEmitter {
29
27
  transport;
30
- protocol;
31
28
  transportPath;
32
29
  bluetoothProps;
33
30
  thp;
34
- transportDescriptorType;
35
- session;
36
- transportSessionOwner;
37
- lastAcquiredHere;
31
+ possibleHIDdevice;
32
+ sessionAcquired;
33
+ _protocol;
34
+ get protocol() {
35
+ return this._protocol;
36
+ }
37
+ getThpState() {
38
+ return this.thp;
39
+ }
38
40
  unreadableError;
39
41
  _firmwareStatus;
40
42
  get firmwareStatus() {
@@ -48,14 +50,13 @@ class Device extends utils_1.TypedEmitter {
48
50
  get features() {
49
51
  return this._features;
50
52
  }
51
- _featuresNeedsReload = false;
53
+ wasUsedElsewhere = false;
52
54
  acquirePromise;
53
55
  releasePromise;
54
56
  runAbort;
55
57
  runPromise;
56
58
  keepTransportSession = false;
57
59
  currentSession;
58
- inconsistent = false;
59
60
  instance = 0;
60
61
  state = [];
61
62
  stateStorage = undefined;
@@ -81,16 +82,28 @@ class Device extends utils_1.TypedEmitter {
81
82
  constructor({ id, transport, descriptor, listener }) {
82
83
  super();
83
84
  this.emitLifecycle = listener;
84
- this.protocol = protocol_1.v1;
85
+ this._protocol = protocol_1.v1;
85
86
  this.uniquePath = id;
86
87
  this.transport = transport;
87
88
  this.transportPath = descriptor.path;
88
- this.transportSessionOwner = descriptor.sessionOwner;
89
- this.transportDescriptorType = descriptor.type;
89
+ this.possibleHIDdevice = [0, 2].includes(descriptor.type);
90
90
  this.bluetoothProps = descriptor.id ? { id: descriptor.id } : undefined;
91
- this.session = descriptor.session;
92
- this.lastAcquiredHere = false;
93
- }
91
+ this.sessionAcquired = null;
92
+ transport.on(transport_1.TRANSPORT.STOPPED, this.onTransportStopped);
93
+ transport.deviceEvents.on(this.transportPath, this.onTransportDeviceEvent);
94
+ }
95
+ onTransportStopped = () => this.disconnect();
96
+ onTransportDeviceEvent = (event) => {
97
+ switch (event.type) {
98
+ case transport_1.TRANSPORT.DEVICE_SESSION_CHANGED:
99
+ return this.updateDescriptor(event.descriptor);
100
+ case transport_1.TRANSPORT.DEVICE_REQUEST_RELEASE:
101
+ return this.usedElsewhere();
102
+ case transport_1.TRANSPORT.DEVICE_DISCONNECTED: {
103
+ return this.disconnect();
104
+ }
105
+ }
106
+ };
94
107
  getSessionChangePromise() {
95
108
  if (!this.sessionDfd) {
96
109
  this.sessionDfd = (0, utils_1.createDeferred)();
@@ -123,15 +136,15 @@ class Device extends utils_1.TypedEmitter {
123
136
  }
124
137
  acquire() {
125
138
  const sessionPromise = this.getSessionChangePromise();
139
+ const previous = this.transport.getDescriptor(this.transportPath)?.session ?? null;
126
140
  this.acquirePromise = this.transport
127
- .acquire({ input: { path: this.transportPath, previous: this.session } })
141
+ .acquire({ input: { path: this.transportPath, previous } })
128
142
  .then(result => this.waitAndCompareSession(result, sessionPromise))
129
143
  .then(result => {
130
144
  if (result.success) {
131
- this.session = result.payload;
132
- this.lastAcquiredHere = true;
133
- this.currentSession?.dispose();
134
- this.currentSession = new DeviceCurrentSession_1.DeviceCurrentSession(this, this.transport, this.protocol, this.session);
145
+ this.wasUsedElsewhere = false;
146
+ this.sessionAcquired = result.payload;
147
+ this.currentSession = new DeviceCurrentSession_1.DeviceCurrentSession(this, this.transport, this.sessionAcquired);
135
148
  return result;
136
149
  }
137
150
  else {
@@ -143,19 +156,17 @@ class Device extends utils_1.TypedEmitter {
143
156
  });
144
157
  return this.acquirePromise;
145
158
  }
146
- async release() {
147
- const localSession = this.lastAcquiredHere ? this.session : null;
148
- if (!localSession || this.keepTransportSession || this.releasePromise) {
159
+ release() {
160
+ if (!this.sessionAcquired || this.keepTransportSession || this.releasePromise) {
149
161
  return;
150
162
  }
151
- await this.currentSession?.dispose();
152
163
  const sessionPromise = this.getSessionChangePromise();
153
164
  this.releasePromise = this.transport
154
- .release({ session: localSession, path: this.transportPath })
165
+ .release({ session: this.sessionAcquired, path: this.transportPath })
155
166
  .then(result => this.waitAndCompareSession(result, sessionPromise))
156
167
  .then(result => {
157
168
  if (result.success) {
158
- this.session = null;
169
+ this.sessionAcquired = null;
159
170
  }
160
171
  return result;
161
172
  })
@@ -184,15 +195,17 @@ class Device extends utils_1.TypedEmitter {
184
195
  error.message === transport_1.TRANSPORT_ERROR.DEVICE_DISCONNECTED_DURING_ACTION ||
185
196
  error.message === transport_1.TRANSPORT_ERROR.HTTP_ERROR) {
186
197
  }
187
- else if (error.message === transport_1.TRANSPORT_ERROR.UNEXPECTED_ERROR ||
188
- error.message === transport_1.TRANSPORT_ERROR.SESSION_WRONG_PREVIOUS ||
189
- error.code === 'Device_UsedElsewhere' ||
190
- error.code === 'Device_InitializeFailed') {
198
+ else if ((this.possibleHIDdevice &&
199
+ error.message === transport_1.TRANSPORT_ERROR.INTERFACE_UNABLE_TO_OPEN_DEVICE) ||
200
+ error.message === transport_1.TRANSPORT_ERROR.LIBUSB_ERROR_ACCESS) {
201
+ this.unreadableError = error.message;
191
202
  this.emitLifecycle(events_1.DEVICE.CONNECT_UNACQUIRED);
192
203
  }
193
204
  else if (error.message === transport_1.TRANSPORT_ERROR.INTERFACE_UNABLE_TO_OPEN_DEVICE ||
194
- error.message?.indexOf(constants_1.ERRORS.LIBUSB_ERROR_MESSAGE) >= 0) {
195
- this.unreadableError = error?.message;
205
+ error.message === transport_1.TRANSPORT_ERROR.UNEXPECTED_ERROR ||
206
+ error.message === transport_1.TRANSPORT_ERROR.SESSION_WRONG_PREVIOUS ||
207
+ error.code === 'Device_UsedElsewhere' ||
208
+ error.code === 'Device_InitializeFailed') {
196
209
  this.emitLifecycle(events_1.DEVICE.CONNECT_UNACQUIRED);
197
210
  }
198
211
  else {
@@ -208,17 +221,12 @@ class Device extends utils_1.TypedEmitter {
208
221
  async updateDescriptor(descriptor) {
209
222
  this.sessionDfd?.resolve(descriptor.session);
210
223
  await Promise.all([this.acquirePromise, this.releasePromise]);
211
- if (descriptor.session && descriptor.session !== this.session) {
224
+ if (descriptor.session && descriptor.session !== this.sessionAcquired) {
212
225
  this.usedElsewhere();
213
226
  }
214
227
  if (!descriptor.session) {
215
- const methodStillRunning = !this.currentSession?.isDisposed();
216
- if (methodStillRunning) {
217
- this.keepTransportSession = false;
218
- }
228
+ this.keepTransportSession = false;
219
229
  }
220
- this.session = descriptor.session;
221
- this.transportSessionOwner = descriptor.sessionOwner;
222
230
  this.emitLifecycle(events_1.DEVICE.CHANGED);
223
231
  }
224
232
  run(fn, options = {}) {
@@ -230,7 +238,7 @@ class Device extends utils_1.TypedEmitter {
230
238
  this.runAbort = new AbortController();
231
239
  const { signal } = this.runAbort;
232
240
  this.runPromise = Promise.race([
233
- this._runInner(fn, options),
241
+ this._runInner(fn, options, signal),
234
242
  new Promise((_, reject) => {
235
243
  signal.addEventListener('abort', () => reject(signal.reason));
236
244
  }),
@@ -253,7 +261,7 @@ class Device extends utils_1.TypedEmitter {
253
261
  return this.runPromise;
254
262
  }
255
263
  async interrupt(reason) {
256
- await this.currentSession?.abort(reason, true);
264
+ await this.currentSession?.abort(reason);
257
265
  this.runAbort?.abort(reason);
258
266
  await this.currentRun;
259
267
  }
@@ -261,19 +269,16 @@ class Device extends utils_1.TypedEmitter {
261
269
  return this.runPromise?.catch(() => { });
262
270
  }
263
271
  usedElsewhere() {
264
- if (!this.lastAcquiredHere) {
272
+ this.wasUsedElsewhere = true;
273
+ if (!this.sessionAcquired) {
265
274
  return;
266
275
  }
267
- this.lastAcquiredHere = false;
268
- this._featuresNeedsReload = true;
276
+ this.transport.releaseDevice(this.sessionAcquired);
277
+ this.sessionAcquired = null;
269
278
  _log.debug('interruptionFromOutside');
270
- this.currentSession?.dispose();
271
279
  this.runAbort?.abort(constants_1.ERRORS.TypedError('Device_UsedElsewhere'));
272
- if (this.session) {
273
- this.transport.releaseDevice(this.session);
274
- }
275
280
  }
276
- async _runInner(fn, options) {
281
+ async _runInner(fn, options, abortSignal) {
277
282
  if (this.releasePromise) {
278
283
  await this.releasePromise;
279
284
  }
@@ -281,6 +286,8 @@ class Device extends utils_1.TypedEmitter {
281
286
  if (acquireNeeded) {
282
287
  await this.acquire();
283
288
  }
289
+ if (abortSignal.aborted)
290
+ throw abortSignal.reason;
284
291
  const { staticSessionId, deriveCardano } = this.getState() || {};
285
292
  if (acquireNeeded || !staticSessionId || (!deriveCardano && options.useCardanoDerivation)) {
286
293
  try {
@@ -289,43 +296,21 @@ class Device extends utils_1.TypedEmitter {
289
296
  }
290
297
  else {
291
298
  const isNative = DataManager_1.DataManager.getSettings('env') === 'react-native';
292
- const getFeaturesTimeout = isNative
293
- ? exports.GET_FEATURES_TIMEOUT_REACT_NATIVE
294
- : exports.GET_FEATURES_TIMEOUT;
295
299
  const cancelTimeout = isNative
296
300
  ? exports.GET_FEATURES_TIMEOUT_REACT_NATIVE
297
301
  : exports.CANCEL_TIMEOUT;
298
- if (['v1', 'bridge'].includes(this.protocol.name) &&
299
- ![0, 2].includes(this.transportDescriptorType)) {
302
+ if (['v1', 'bridge'].includes(this.protocol.name) && !this.possibleHIDdevice) {
300
303
  _log.debug('sending a preventive cancel on the first encounter with the device');
301
304
  await Promise.race([
302
305
  this.getCurrentSession().cancelCall(),
303
306
  new Promise((_, reject) => setTimeout(reject, cancelTimeout)),
304
307
  ]).catch(() => this.acquire());
305
308
  }
306
- let getFeaturesTimeoutId;
307
- await Promise.race([
308
- this.getFeatures().finally(() => clearTimeout(getFeaturesTimeoutId)),
309
- new Promise((_resolve, reject) => {
310
- getFeaturesTimeoutId = setTimeout(async () => {
311
- await this.getCurrentSession().cancelCall(false);
312
- reject(new Error('GetFeatures timeout'));
313
- }, getFeaturesTimeout);
314
- }),
315
- ]);
309
+ await this.getFeatures();
316
310
  }
317
311
  }
318
312
  catch (error) {
319
313
  _log.warn('Device._runInner error: ', error.message);
320
- if (!this.inconsistent &&
321
- (error.message === 'GetFeatures timeout' || error.message === 'Unknown message')) {
322
- this.inconsistent = true;
323
- return this._runInner(() => Promise.resolve({}), options);
324
- }
325
- if (transport_1.TRANSPORT_ERROR.ABORTED_BY_TIMEOUT === error.message) {
326
- this.unreadableError = 'Connection timeout';
327
- }
328
- this.inconsistent = true;
329
314
  return Promise.reject(constants_1.ERRORS.TypedError('Device_InitializeFailed', `Initialize failed: ${error.message}${error.code ? `, code: ${error.code}` : ''}`));
330
315
  }
331
316
  }
@@ -372,7 +357,7 @@ class Device extends utils_1.TypedEmitter {
372
357
  setInstance(instance = 0) {
373
358
  if (this.instance !== instance) {
374
359
  if (this.keepTransportSession) {
375
- this.lastAcquiredHere = false;
360
+ this.sessionAcquired = null;
376
361
  this.keepTransportSession = false;
377
362
  }
378
363
  }
@@ -398,31 +383,6 @@ class Device extends utils_1.TypedEmitter {
398
383
  this.stateStorage?.saveState(this, newState);
399
384
  }
400
385
  }
401
- async validateState(preauthorized = false) {
402
- if (!this.features)
403
- return;
404
- if (!this.features.unlocked && preauthorized) {
405
- if (await this.getCommands().preauthorize(false)) {
406
- return;
407
- }
408
- }
409
- const expectedState = this.getState()?.staticSessionId;
410
- const { message } = await this.getCurrentSession().typedCall('GetAddress', 'Address', {
411
- address_n: [(0, pathUtils_1.toHardened)(44), (0, pathUtils_1.toHardened)(1), (0, pathUtils_1.toHardened)(0), 0, 0],
412
- coin_name: 'Testnet',
413
- script_type: 'SPENDADDRESS',
414
- });
415
- const uniqueState = `${message.address}@${this.features.device_id}:${this.instance}`;
416
- if (this.features.session_id) {
417
- this.setState({ sessionId: this.features.session_id });
418
- }
419
- if (expectedState && expectedState !== uniqueState) {
420
- return uniqueState;
421
- }
422
- if (!expectedState) {
423
- this.setState({ staticSessionId: uniqueState });
424
- }
425
- }
426
386
  async initialize(useCardanoDerivation) {
427
387
  let payload;
428
388
  if (this.features) {
@@ -570,12 +530,12 @@ class Device extends utils_1.TypedEmitter {
570
530
  return response.message;
571
531
  }
572
532
  const length = payload.byteLength;
573
- let response = await this.getCurrentSession().typedCall('ChangeLanguage', ['TranslationDataRequest', 'Success'], { data_length: length });
533
+ let response = await this.getCurrentSession().typedCall('ChangeLanguage', ['DataChunkRequest', 'Success'], { data_length: length });
574
534
  while (response.type !== 'Success') {
575
535
  const start = response.message.data_offset;
576
536
  const end = response.message.data_offset + response.message.data_length;
577
537
  const chunk = payload.slice(start, end);
578
- response = await this.getCurrentSession().typedCall('TranslationDataAck', ['TranslationDataRequest', 'Success'], {
538
+ response = await this.getCurrentSession().typedCall('DataChunkAck', ['DataChunkRequest', 'Success'], {
579
539
  data_chunk: Buffer.from(chunk).toString('hex'),
580
540
  });
581
541
  }
@@ -616,7 +576,6 @@ class Device extends utils_1.TypedEmitter {
616
576
  this.availableTranslations = this.firmwareRelease?.translations ?? [];
617
577
  }
618
578
  this._features = feat;
619
- this._featuresNeedsReload = false;
620
579
  if (feat.fw_vendor === 'Trezor Bitcoin-only') {
621
580
  this._firmwareType = types_1.FirmwareType.BitcoinOnly;
622
581
  }
@@ -631,7 +590,7 @@ class Device extends utils_1.TypedEmitter {
631
590
  ? types_1.FirmwareType.BitcoinOnly
632
591
  : types_1.FirmwareType.Regular;
633
592
  }
634
- const deviceInfo = models_1.models[feat.internal_model] ?? {
593
+ const deviceInfo = device_utils_1.models[feat.internal_model] ?? {
635
594
  name: `Unknown ${feat.internal_model}`,
636
595
  colors: {},
637
596
  };
@@ -665,8 +624,14 @@ class Device extends utils_1.TypedEmitter {
665
624
  }
666
625
  disconnect() {
667
626
  _log.debug('Disconnect cleanup');
627
+ this.transport.off(transport_1.TRANSPORT.STOPPED, this.onTransportStopped);
628
+ this.transport.deviceEvents.off(this.transportPath, this.onTransportDeviceEvent);
629
+ this.removeAllListeners();
668
630
  this.sessionDfd?.reject(new Error());
669
- this.lastAcquiredHere = false;
631
+ if (this.sessionAcquired) {
632
+ this.transport.releaseSync(this.sessionAcquired);
633
+ this.sessionAcquired = null;
634
+ }
670
635
  this.emitLifecycle(events_1.DEVICE.DISCONNECT);
671
636
  this.emitLifecycle = () => { };
672
637
  return this.interrupt(constants_1.ERRORS.TypedError('Device_Disconnected'));
@@ -680,9 +645,6 @@ class Device extends utils_1.TypedEmitter {
680
645
  isSeedless() {
681
646
  return this.features && !!this.features.no_backup;
682
647
  }
683
- isInconsistent() {
684
- return this.inconsistent;
685
- }
686
648
  getVersion() {
687
649
  if (!this.features)
688
650
  return;
@@ -700,13 +662,13 @@ class Device extends utils_1.TypedEmitter {
700
662
  return utils_1.versionUtils.isNewerOrEqual(version, modelVersion);
701
663
  }
702
664
  isUsed() {
703
- return typeof this.session === 'string';
665
+ return !!this.transport.getDescriptor(this.transportPath)?.session;
704
666
  }
705
667
  isUsedHere() {
706
- return this.isUsed() && this.lastAcquiredHere;
668
+ return !!this.sessionAcquired;
707
669
  }
708
670
  isUsedElsewhere() {
709
- return this.isUsed() && !this.lastAcquiredHere;
671
+ return this.isUsed() && !this.isUsedHere();
710
672
  }
711
673
  getUniquePath() {
712
674
  return this.uniquePath;
@@ -731,12 +693,6 @@ class Device extends utils_1.TypedEmitter {
731
693
  }
732
694
  return null;
733
695
  }
734
- dispose() {
735
- this.removeAllListeners();
736
- if (this.session && this.lastAcquiredHere) {
737
- this.transport.releaseSync(this.session);
738
- }
739
- }
740
696
  getMode() {
741
697
  if (this.features.bootloader_mode)
742
698
  return 'bootloader';
@@ -755,27 +711,26 @@ class Device extends utils_1.TypedEmitter {
755
711
  type: 'unreadable',
756
712
  error: this.unreadableError,
757
713
  label: 'Unreadable device',
758
- transportDescriptorType: this.transportDescriptorType,
714
+ hid: this.possibleHIDdevice,
759
715
  };
760
716
  }
761
717
  if (this.isUnacquired()) {
718
+ const sessionOwner = this.transport.getDescriptor(this.transportPath)?.sessionOwner;
762
719
  return {
763
720
  ...base,
764
721
  type: 'unacquired',
765
722
  label: 'Unacquired device',
766
723
  name: this.name,
767
- transportSessionOwner: this.lastAcquiredHere
768
- ? undefined
769
- : this.transportSessionOwner,
724
+ transportSessionOwner: this.sessionAcquired ? undefined : sessionOwner,
770
725
  bluetoothProps: this.bluetoothProps,
771
726
  thp: this.thp?.serialize(),
772
727
  };
773
728
  }
774
729
  const defaultLabel = 'My Trezor';
775
730
  const label = this.features.label === '' || !this.features.label ? defaultLabel : this.features.label;
776
- let status = this.isUsedElsewhere() ? 'occupied' : 'available';
777
- if (this._featuresNeedsReload)
778
- status = 'used';
731
+ const status = this.isUsedElsewhere()
732
+ ? 'occupied'
733
+ : (this.wasUsedElsewhere && 'used') || 'available';
779
734
  return {
780
735
  ...base,
781
736
  type: 'acquired',