@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
@@ -3,8 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DeviceCurrentSession = void 0;
4
4
  const protobuf_1 = require("@trezor/protobuf");
5
5
  const schema_utils_1 = require("@trezor/schema-utils");
6
+ const transport_1 = require("@trezor/transport");
7
+ const errors_groups_1 = require("@trezor/transport/lib/errors-groups");
6
8
  const utils_1 = require("@trezor/utils");
7
9
  const constants_1 = require("../constants");
10
+ const DataManager_1 = require("../data/DataManager");
8
11
  const events_1 = require("../events");
9
12
  const debug_1 = require("../utils/debug");
10
13
  const blacklist = {
@@ -15,31 +18,52 @@ const blacklist = {
15
18
  DecryptedMessage: ['message', 'address'],
16
19
  Features: true,
17
20
  };
21
+ const allowedCallsBeforeInitialize = [
22
+ 'Cancel',
23
+ 'Initialize',
24
+ 'GetFeatures',
25
+ 'GetFirmwareHash',
26
+ 'ChangeLanguage',
27
+ 'DataChunkAck',
28
+ 'FirmwareErase',
29
+ 'FirmwareUpload',
30
+ ];
18
31
  const filterForLog = (type, msg) => blacklist[type] === true
19
32
  ? '(redacted...)'
20
33
  : (blacklist[type] ?? []).reduce((prev, cur) => ({ ...prev, [cur]: '(redacted...)' }), msg);
21
34
  const logger = (0, debug_1.initLog)('DeviceCommands');
35
+ const isExpectedResponse = (response, expected) => (Array.isArray(expected) ? expected : expected.split('|')).includes(response.type);
22
36
  const success = (payload) => ({ success: true, payload });
23
37
  const error = (error) => ({ success: false, error });
24
- const fail = (msg, cause) => error(new Error(msg, cause ? { cause } : undefined));
38
+ const fail = (msg) => error(new Error(msg, (0, errors_groups_1.isErrorWithoutDeviceInteraction)(msg) ? { cause: 'transport-error' } : undefined));
39
+ const getFeaturesTimeout = () => DataManager_1.DataManager.getSettings('env') === 'react-native' ? 20_000 : 3_000;
25
40
  class DeviceCurrentSession {
26
41
  device;
27
42
  transport;
28
- protocol;
29
43
  session;
30
44
  disposed;
31
45
  callPromise;
32
46
  abortController;
33
- constructor(device, transport, protocol, session) {
47
+ constructor(device, transport, session) {
34
48
  this.device = device;
35
49
  this.transport = transport;
36
- this.protocol = protocol;
37
50
  this.session = session;
51
+ transport.deviceEvents.once(device.transportPath, e => {
52
+ if (!this.disposed) {
53
+ this.disposed = constants_1.ERRORS.TypedError(e.type === transport_1.TRANSPORT.DEVICE_DISCONNECTED
54
+ ? 'Device_Disconnected'
55
+ : 'Device_UsedElsewhere');
56
+ this.abortController?.abort(this.disposed);
57
+ }
58
+ });
38
59
  }
39
60
  isDisposed() {
40
61
  return !!this.disposed;
41
62
  }
42
- async typedCall(type, resType, msg = {}) {
63
+ async typedCall(type, expectedType, msg = {}) {
64
+ if (!allowedCallsBeforeInitialize.includes(type) && !this.device?.features?.session_id) {
65
+ console.error('Runtime', `typedCall: Device not initialized when calling ${type}. call Initialize first`);
66
+ }
43
67
  (0, schema_utils_1.Assert)(protobuf_1.MessagesSchema.MessageType.properties[type], msg);
44
68
  this.abortController = new AbortController();
45
69
  const { signal } = this.abortController;
@@ -52,15 +76,19 @@ class DeviceCurrentSession {
52
76
  if (!response.success)
53
77
  throw response.error;
54
78
  const { payload } = response;
55
- if (!(Array.isArray(resType) ? resType : resType.split('|')).includes(payload.type)) {
79
+ const receivedType = payload.type;
80
+ if (isExpectedResponse(payload, expectedType)) {
81
+ return payload;
82
+ }
83
+ else {
56
84
  await (0, utils_1.scheduleAction)(abort => this.transport.receive({
57
85
  session: this.session,
58
- protocol: this.protocol,
86
+ protocol: this.device.protocol,
87
+ thpState: this.device.getThpState(),
59
88
  signal: abort,
60
89
  }), { timeout: 500 }).catch(() => { });
61
- throw constants_1.ERRORS.TypedError('Runtime', `assertType: Response of unexpected type: ${payload.type}. Should be ${resType}`);
90
+ throw constants_1.ERRORS.TypedError('Runtime', `assertType: Response of unexpected type: ${receivedType}. Should be ${expectedType}`);
62
91
  }
63
- return payload;
64
92
  }
65
93
  needCancelWorkaround() {
66
94
  return (this.transport.name === 'BridgeTransport' &&
@@ -68,32 +96,36 @@ class DeviceCurrentSession {
68
96
  }
69
97
  async callLoop(type, msg, abortPromise) {
70
98
  let [name, data] = [type, msg];
71
- const { protocol, session } = this;
72
99
  let pinUnlocked = false;
73
100
  while (true) {
74
- if (this.disposed)
75
- return error(this.disposed);
76
- const callPromise = this.call({ session, name, data, protocol });
77
- const abortedDuringCall = await Promise.race([
78
- callPromise.then(() => false),
79
- abortPromise.then(() => true),
101
+ const timeout = name === 'GetFeatures' ? getFeaturesTimeout() : undefined;
102
+ const callPromise = this.call(name, data, timeout);
103
+ const [abortedDuringCall, response] = await Promise.race([
104
+ callPromise.then(res => [false, res]),
105
+ abortPromise.then(res => [true, res]),
80
106
  ]);
81
107
  if (name === 'ButtonAck' && abortedDuringCall && !this.disposed) {
82
108
  if (this.needCancelWorkaround()) {
83
109
  try {
84
110
  await (0, utils_1.resolveAfter)(1);
85
111
  await this.device.acquire();
86
- await this.device.getCurrentSession().cancelCall(false);
112
+ await this.device.getCurrentSession().cancelCall();
87
113
  await this.device.release();
88
114
  }
89
115
  catch {
90
116
  }
91
117
  }
92
118
  else {
93
- await this.cancelCall(false);
119
+ await this.transport.send({
120
+ name: 'Cancel',
121
+ data: {},
122
+ session: this.session,
123
+ protocol: this.device.protocol,
124
+ thpState: this.device.getThpState(),
125
+ });
94
126
  }
95
127
  }
96
- const response = await callPromise;
128
+ await callPromise;
97
129
  if (this.disposed)
98
130
  return error(this.disposed);
99
131
  if (!response.success)
@@ -102,6 +134,10 @@ class DeviceCurrentSession {
102
134
  switch (res.type) {
103
135
  case 'Failure': {
104
136
  const { code, message } = res.message;
137
+ if (name === 'GetFeatures' && code === 'Failure_UnexpectedMessage') {
138
+ [name, data] = ['Initialize', {}];
139
+ break;
140
+ }
105
141
  const err = message ||
106
142
  (code === 'Failure_FirmwareError' && 'Firmware installation failed') ||
107
143
  (code === 'Failure_ActionCancelled' && 'Action cancelled by user') ||
@@ -127,7 +163,7 @@ class DeviceCurrentSession {
127
163
  abortPromise,
128
164
  ]);
129
165
  if (!promptRes.success) {
130
- const cancelRes = await this.cancelCall();
166
+ const cancelRes = await this.call('Cancel', {});
131
167
  return cancelRes.success ? promptRes : cancelRes;
132
168
  }
133
169
  pinUnlocked = true;
@@ -140,7 +176,7 @@ class DeviceCurrentSession {
140
176
  abortPromise,
141
177
  ]);
142
178
  if (!promptRes.success) {
143
- const cancelRes = await this.cancelCall();
179
+ const cancelRes = await this.call('Cancel', {});
144
180
  return cancelRes.success ? promptRes : cancelRes;
145
181
  }
146
182
  const payload = promptRes.payload.passphraseOnDevice
@@ -155,7 +191,7 @@ class DeviceCurrentSession {
155
191
  abortPromise,
156
192
  ]);
157
193
  if (!promptRes.success) {
158
- const cancelRes = await this.cancelCall();
194
+ const cancelRes = await this.call('Cancel', {});
159
195
  return cancelRes.success ? promptRes : cancelRes;
160
196
  }
161
197
  [name, data] = ['WordAck', { word: promptRes.payload }];
@@ -170,9 +206,18 @@ class DeviceCurrentSession {
170
206
  }
171
207
  }
172
208
  }
173
- async call(params) {
174
- logger.debug('Sending', params.name, filterForLog(params.name, params.data));
175
- const result = await this.transport.call(params);
209
+ async call(name, data, timeout) {
210
+ if (this.disposed)
211
+ return Promise.resolve(error(this.disposed));
212
+ logger.debug('Sending', name, filterForLog(name, data));
213
+ const result = await this.transport.call({
214
+ name,
215
+ data,
216
+ session: this.session,
217
+ protocol: this.device.protocol,
218
+ thpState: this.device.getThpState(),
219
+ timeout,
220
+ });
176
221
  if (result.success) {
177
222
  const { type, message } = result.payload;
178
223
  logger.debug('Received', type, filterForLog(type, message));
@@ -180,29 +225,15 @@ class DeviceCurrentSession {
180
225
  else {
181
226
  logger.warn('Received transport error', result.error, result.message);
182
227
  }
183
- return result.success ? success(result.payload) : fail(result.error, 'transport-error');
228
+ return result.success ? success(result.payload) : fail(result.error);
184
229
  }
185
- async cancelCall(expectResponse = true) {
186
- if (this.disposed)
187
- return Promise.resolve(error(this.disposed));
188
- const { protocol, session } = this;
189
- const cancelArgs = { session, name: 'Cancel', data: {}, protocol };
190
- const response = expectResponse
191
- ? await this.transport.call(cancelArgs)
192
- : await this.transport.send(cancelArgs);
193
- return response.success ? success(response.payload) : fail(response.error);
230
+ cancelCall() {
231
+ return this.call('Cancel', {});
194
232
  }
195
- async abort(reason, dispose = false) {
233
+ async abort(reason) {
196
234
  this.abortController?.abort(reason);
197
235
  await this.callPromise;
198
- if (dispose)
199
- this.disposed = reason;
200
- }
201
- async dispose() {
202
- if (!this.disposed) {
203
- this.disposed = constants_1.ERRORS.TypedError('Runtime', 'typedCall: DeviceCommands already disposed');
204
- await this.abort(this.disposed);
205
- }
236
+ this.disposed = reason;
206
237
  }
207
238
  }
208
239
  exports.DeviceCurrentSession = DeviceCurrentSession;
@@ -23,14 +23,14 @@ export interface IDeviceList {
23
23
  dispose: DeviceList['dispose'];
24
24
  }
25
25
  export declare const assertDeviceListConnected: (deviceList: IDeviceList) => asserts deviceList is DeviceList;
26
- type ConstructorParams = Pick<ConnectSettings, 'priority' | 'debug' | '_sessionsBackgroundUrl' | 'manifest'> & {
26
+ type ConstructorParams = Pick<ConnectSettings, 'priority' | 'debug' | 'manifest'> & {
27
27
  messages: Record<string, any>;
28
28
  };
29
29
  type InitParams = Pick<ConnectSettings, 'transports' | 'pendingTransportEvent' | 'transportReconnect'>;
30
30
  export declare class DeviceList extends TypedEmitter<DeviceListEvents> implements IDeviceList {
31
31
  private readonly transportManagers;
32
32
  private transports;
33
- private readonly devices;
33
+ private devices;
34
34
  private deviceCounter;
35
35
  private readonly handshakeLock;
36
36
  private readonly authPenaltyManager;
@@ -44,14 +44,10 @@ export declare class DeviceList extends TypedEmitter<DeviceListEvents> implement
44
44
  version: string;
45
45
  outdated: boolean;
46
46
  }[];
47
- constructor({ messages, priority, debug, _sessionsBackgroundUrl, manifest, }: ConstructorParams);
47
+ constructor({ messages, priority, debug, manifest }: ConstructorParams);
48
48
  private onDeviceConnected;
49
- private onDeviceDisconnected;
50
- private onDeviceSessionChanged;
51
- private onDeviceRequestRelease;
52
49
  private getOrCreateTransportManager;
53
50
  init({ transports, transportReconnect, pendingTransportEvent }?: InitParams): Promise<void>;
54
- private startTransport;
55
51
  private initializeTransport;
56
52
  private waitForDevices;
57
53
  getDeviceCount(): number;
@@ -60,7 +56,6 @@ export declare class DeviceList extends TypedEmitter<DeviceListEvents> implement
60
56
  getDeviceByPath(path: DeviceUniquePath): Device | undefined;
61
57
  getDeviceByStaticState(state: StaticSessionId): Device | undefined;
62
58
  dispose(): Promise<void>;
63
- private stopTransport;
64
59
  enumerate(): Promise<void>;
65
60
  addAuthPenalty(device: Device): void;
66
61
  removeAuthPenalty(device: Device): void;
@@ -30,30 +30,6 @@ const createAuthPenaltyManager = (priority) => {
30
30
  const clear = () => Object.keys(penalizedDevices).forEach(key => delete penalizedDevices[key]);
31
31
  return { get, add, remove, clear };
32
32
  };
33
- const createDeviceCollection = () => {
34
- let devices = [];
35
- const isEqual = (a) => (b) => a.transport === b.transport && a.transportPath === b.transportPath;
36
- const get = (transportPath, transport) => devices.find(isEqual({ transport, transportPath }));
37
- const all = () => devices;
38
- const add = (device) => {
39
- const index = devices.findIndex(isEqual(device));
40
- if (index >= 0)
41
- devices[index] = device;
42
- else
43
- devices.push(device);
44
- };
45
- const remove = (transportPath, transport) => {
46
- const index = devices.findIndex(isEqual({ transport, transportPath }));
47
- const [removed] = index >= 0 ? devices.splice(index, 1) : [undefined];
48
- return removed;
49
- };
50
- const clear = (transport) => {
51
- let removed;
52
- [removed, devices] = (0, utils_1.arrayPartition)(devices, d => !transport || d.transport === transport);
53
- return removed;
54
- };
55
- return { get, all, add, remove, clear };
56
- };
57
33
  const getTransportInfo = (transport) => ({
58
34
  apiType: transport.apiType,
59
35
  type: transport.name,
@@ -69,7 +45,7 @@ exports.assertDeviceListConnected = assertDeviceListConnected;
69
45
  class DeviceList extends utils_1.TypedEmitter {
70
46
  transportManagers = {};
71
47
  transports = [];
72
- devices = createDeviceCollection();
48
+ devices = [];
73
49
  deviceCounter = Date.now();
74
50
  handshakeLock;
75
51
  authPenaltyManager;
@@ -92,7 +68,7 @@ class DeviceList extends utils_1.TypedEmitter {
92
68
  getActiveTransports() {
93
69
  return this.getConnectedTransports().map(getTransportInfo);
94
70
  }
95
- constructor({ messages, priority, debug, _sessionsBackgroundUrl, manifest, }) {
71
+ constructor({ messages, priority, debug, manifest }) {
96
72
  super();
97
73
  const transportLogger = (0, debug_1.initLog)('@trezor/transport', debug);
98
74
  this.handshakeLock = (0, utils_1.getSynchronize)();
@@ -100,7 +76,6 @@ class DeviceList extends utils_1.TypedEmitter {
100
76
  this.updateTransports = (0, TransportList_1.createTransportList)({
101
77
  messages,
102
78
  logger: transportLogger,
103
- sessionsBackgroundUrl: _sessionsBackgroundUrl,
104
79
  id: manifest?.appName || manifest?.appUrl || 'unknown app',
105
80
  });
106
81
  }
@@ -110,34 +85,27 @@ class DeviceList extends utils_1.TypedEmitter {
110
85
  id: (0, types_1.DeviceUniquePath)(id),
111
86
  transport,
112
87
  descriptor,
113
- listener: lifecycle => this.emit(lifecycle, device),
88
+ listener: lifecycle => {
89
+ if (lifecycle === events_1.DEVICE.DISCONNECT) {
90
+ this.authPenaltyManager.remove(device);
91
+ const index = this.devices.indexOf(device);
92
+ if (index >= 0)
93
+ this.devices.splice(index, 1);
94
+ }
95
+ this.emit(lifecycle, device);
96
+ },
114
97
  });
115
- this.devices.add(device);
98
+ this.devices.push(device);
116
99
  const penalty = this.authPenaltyManager.get();
117
100
  this.handshakeLock(async () => {
118
- if (this.devices.get(descriptor.path, transport)) {
101
+ if (this.devices.includes(device)) {
119
102
  await device.handshake(penalty);
120
103
  }
121
104
  });
122
105
  }
123
- onDeviceDisconnected(descriptor, transport) {
124
- const device = this.devices.remove(descriptor.path, transport);
125
- device?.disconnect();
126
- }
127
- onDeviceSessionChanged(descriptor, transport) {
128
- const device = this.devices.get(descriptor.path, transport);
129
- device?.updateDescriptor(descriptor);
130
- }
131
- onDeviceRequestRelease(descriptor, transport) {
132
- const device = this.devices.get(descriptor.path, transport);
133
- device?.usedElsewhere();
134
- }
135
106
  getOrCreateTransportManager(apiType) {
136
107
  if (!this.transportManagers[apiType]) {
137
- const manager = new TransportManager_1.TransportManager({
138
- startTransport: this.startTransport.bind(this),
139
- stopTransport: this.stopTransport.bind(this),
140
- });
108
+ const manager = new TransportManager_1.TransportManager(this.initializeTransport.bind(this));
141
109
  manager.on(transport_1.TRANSPORT.START, transport => this.emit(transport_1.TRANSPORT.START, getTransportInfo(transport)));
142
110
  manager.on(transport_1.TRANSPORT.ERROR, error => this.emit(transport_1.TRANSPORT.ERROR, { apiType, error }));
143
111
  this.transportManagers[apiType] = manager;
@@ -157,20 +125,8 @@ class DeviceList extends utils_1.TypedEmitter {
157
125
  }));
158
126
  await Promise.all(promises);
159
127
  }
160
- async startTransport(transport, pendingTransportEvent, signal) {
161
- try {
162
- await this.initializeTransport(transport, pendingTransportEvent, signal);
163
- }
164
- catch (err) {
165
- this.stopTransport(transport);
166
- throw err;
167
- }
168
- }
169
128
  async initializeTransport(transport, pendingTransportEvent, signal) {
170
129
  transport.on(transport_1.TRANSPORT.DEVICE_CONNECTED, d => this.onDeviceConnected(d, transport));
171
- transport.on(transport_1.TRANSPORT.DEVICE_DISCONNECTED, d => this.onDeviceDisconnected(d, transport));
172
- transport.on(transport_1.TRANSPORT.DEVICE_SESSION_CHANGED, d => this.onDeviceSessionChanged(d, transport));
173
- transport.on(transport_1.TRANSPORT.DEVICE_REQUEST_RELEASE, d => this.onDeviceRequestRelease(d, transport));
174
130
  const enumerateResult = await transport.enumerate({ signal });
175
131
  if (!enumerateResult.success) {
176
132
  throw new Error(enumerateResult.error);
@@ -211,44 +167,32 @@ class DeviceList extends utils_1.TypedEmitter {
211
167
  });
212
168
  }
213
169
  getDeviceCount() {
214
- return this.devices.all().length;
170
+ return this.devices.length;
215
171
  }
216
172
  getAllDevices() {
217
- return this.devices.all();
173
+ return this.devices;
218
174
  }
219
175
  getOnlyDevice() {
220
- return this.getDeviceCount() === 1 ? this.devices.all()[0] : undefined;
176
+ return this.devices.length === 1 ? this.devices[0] : undefined;
221
177
  }
222
178
  getDeviceByPath(path) {
223
- return this.devices.all().find(d => d.getUniquePath() === path);
179
+ return this.devices.find(d => d.getUniquePath() === path);
224
180
  }
225
181
  getDeviceByStaticState(state) {
226
182
  const deviceId = state.split('@')[1].split(':')[0];
227
- return this.devices.all().find(d => d.features?.device_id === deviceId);
183
+ return this.devices.find(d => d.features?.device_id === deviceId);
228
184
  }
229
185
  async dispose() {
230
186
  this.removeAllListeners();
231
187
  const promises = Object.values(this.transportManagers).map(manager => manager.dispose());
232
188
  await Promise.all(promises);
233
189
  }
234
- stopTransport(transport) {
235
- const devices = this.devices.clear(transport);
236
- devices.forEach(device => {
237
- this.emit(events_1.DEVICE.DISCONNECT, device);
238
- this.authPenaltyManager.remove(device);
239
- device.dispose();
240
- });
241
- transport?.stop();
242
- }
243
190
  async enumerate() {
244
191
  const promises = this.getConnectedTransports().map(async (transport) => {
245
192
  const res = await transport.enumerate();
246
- if (!res.success) {
247
- return;
193
+ if (res.success) {
194
+ transport.handleDescriptorsChange(res.payload);
248
195
  }
249
- res.payload.forEach(d => {
250
- this.devices.get(d.path, transport)?.updateDescriptor(d);
251
- });
252
196
  });
253
197
  await Promise.all(promises);
254
198
  }
@@ -4,10 +4,7 @@ type TransportManagerEvents = {
4
4
  [TRANSPORT.START]: Transport;
5
5
  [TRANSPORT.ERROR]: string;
6
6
  };
7
- type TransportManagerParams = {
8
- startTransport: (transport: Transport, pendingTransportEvent: boolean, signal: AbortSignal) => Promise<void>;
9
- stopTransport: (transport: Transport) => void;
10
- };
7
+ type StartTransport = (transport: Transport, pendingTransportEvent: boolean, signal: AbortSignal) => Promise<void>;
11
8
  type InitParams = {
12
9
  transports: Transport[];
13
10
  transportReconnect?: boolean;
@@ -20,8 +17,7 @@ export declare class TransportManager extends TypedEmitter<TransportManagerEvent
20
17
  private transportReconnect;
21
18
  private upgradeTimeout?;
22
19
  private readonly startTransport;
23
- private readonly stopTransport;
24
- constructor({ startTransport, stopTransport }: TransportManagerParams);
20
+ constructor(startTransport: StartTransport);
25
21
  pending(): Promise<void> | undefined;
26
22
  get(): Transport | undefined;
27
23
  init({ transports, transportReconnect, pendingTransportEvent }: InitParams): Promise<void>;
@@ -34,11 +34,9 @@ class TransportManager extends utils_1.TypedEmitter {
34
34
  transportReconnect = false;
35
35
  upgradeTimeout;
36
36
  startTransport;
37
- stopTransport;
38
- constructor({ startTransport, stopTransport }) {
37
+ constructor(startTransport) {
39
38
  super();
40
39
  this.startTransport = startTransport;
41
- this.stopTransport = stopTransport;
42
40
  }
43
41
  pending() {
44
42
  return this.lock.getPending();
@@ -58,7 +56,7 @@ class TransportManager extends utils_1.TypedEmitter {
58
56
  if (activeTransport) {
59
57
  clearTimeout(this.upgradeTimeout);
60
58
  delete this.activeTransport;
61
- this.stopTransport(activeTransport);
59
+ activeTransport.stop();
62
60
  }
63
61
  return Promise.resolve();
64
62
  });
@@ -104,17 +102,23 @@ class TransportManager extends utils_1.TypedEmitter {
104
102
  if (activeTransport) {
105
103
  clearTimeout(this.upgradeTimeout);
106
104
  delete this.activeTransport;
107
- this.stopTransport(activeTransport);
105
+ activeTransport.stop();
108
106
  }
109
107
  if (transport) {
110
- await this.startTransport(transport, pendingTransportEvent, abortSignal);
108
+ try {
109
+ await this.startTransport(transport, pendingTransportEvent, abortSignal);
110
+ }
111
+ catch (err) {
112
+ transport.stop();
113
+ throw err;
114
+ }
111
115
  transport.on(transport_1.TRANSPORT.ERROR, error => {
112
116
  this.emit(transport_1.TRANSPORT.ERROR, error);
113
117
  clearTimeout(this.upgradeTimeout);
114
118
  this.lock
115
119
  .override('Transport error', async (signal) => {
116
120
  delete this.activeTransport;
117
- this.stopTransport(transport);
121
+ transport.stop();
118
122
  if (this.transportReconnect) {
119
123
  await (0, utils_1.resolveAfter)(1000, signal);
120
124
  await this.createInitPromise(pendingTransportEvent, signal);
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.checkFirmwareRevision = void 0;
4
4
  const utils_1 = require("@trezor/utils");
5
5
  const calculateRevisionForDevice_1 = require("./calculateRevisionForDevice");
6
- const downloadReleasesMetadata_1 = require("../data/downloadReleasesMetadata");
6
+ const firmwareInfo_1 = require("../data/firmwareInfo");
7
7
  const assets_browser_1 = require("../utils/assets-browser");
8
8
  const isNotFoundError = (e) => e instanceof assets_browser_1.HttpRequestError && e.response.status === 404;
9
9
  const isNodeJSOfflineError = (e) => ['FetchError', 'AbortError'].includes(e.name);
@@ -14,7 +14,7 @@ const isOfflineError = (e) => {
14
14
  return isNodeJSOfflineError(e) || isReactNativeOfflineError(e);
15
15
  };
16
16
  const getOnlineReleaseMetadata = async ({ firmwareVersion, internalModel, }) => {
17
- const onlineReleases = await (0, downloadReleasesMetadata_1.downloadReleasesMetadata)({ internal_model: internalModel });
17
+ const onlineReleases = await (0, firmwareInfo_1.getOnlineReleases)(internalModel);
18
18
  return onlineReleases.find(onlineRelease => utils_1.versionUtils.isEqual(onlineRelease.version, firmwareVersion));
19
19
  };
20
20
  const failFirmwareRevisionCheck = (error, errorPayload) => ({
@@ -0,0 +1,2 @@
1
+ export { validateState } from './validateState';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateState = void 0;
4
+ var validateState_1 = require("./validateState");
5
+ Object.defineProperty(exports, "validateState", { enumerable: true, get: function () { return validateState_1.validateState; } });
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ import { WorkflowContext } from '../../types/workflow';
2
+ export declare const validateState: (context: WorkflowContext) => Promise<undefined>;
3
+ //# sourceMappingURL=validateState.d.ts.map
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateState = void 0;
4
+ const constants_1 = require("../../constants");
5
+ const DataManager_1 = require("../../data/DataManager");
6
+ const events_1 = require("../../events");
7
+ const pathUtils_1 = require("../../utils/pathUtils");
8
+ const getState = async ({ device, method }) => {
9
+ if (!device.features)
10
+ return;
11
+ if (!device.features.unlocked && method.preauthorized) {
12
+ if (await device.getCommands().preauthorize(false)) {
13
+ return;
14
+ }
15
+ }
16
+ const expectedState = device.getState()?.staticSessionId;
17
+ const { message } = await device.getCurrentSession().typedCall('GetAddress', 'Address', {
18
+ address_n: [(0, pathUtils_1.toHardened)(44), (0, pathUtils_1.toHardened)(1), (0, pathUtils_1.toHardened)(0), 0, 0],
19
+ coin_name: 'Testnet',
20
+ script_type: 'SPENDADDRESS',
21
+ });
22
+ const uniqueState = `${message.address}@${device.features.device_id}:${device.getInstance()}`;
23
+ if (device.features.session_id) {
24
+ device.setState({ sessionId: device.features.session_id });
25
+ }
26
+ if (expectedState && expectedState !== uniqueState) {
27
+ return uniqueState;
28
+ }
29
+ if (!expectedState) {
30
+ device.setState({ staticSessionId: uniqueState });
31
+ }
32
+ };
33
+ const MAX_PIN_TRIES = 3;
34
+ const getInvalidDeviceState = async (context) => {
35
+ for (let i = 0; i < MAX_PIN_TRIES - 1; ++i) {
36
+ try {
37
+ return await getState(context);
38
+ }
39
+ catch (error) {
40
+ if (error.message.includes('PIN invalid')) {
41
+ context.method.postMessage((0, events_1.createUiMessage)(events_1.UI.INVALID_PIN, { device: context.device.toMessageObject() }));
42
+ }
43
+ else {
44
+ throw error;
45
+ }
46
+ }
47
+ }
48
+ return getState(context).catch(error => {
49
+ if (error.message.includes('PIN invalid')) {
50
+ context.method.postMessage((0, events_1.createUiMessage)(events_1.UI.INVALID_PIN_ATTEMPTS_DEPLETED, {
51
+ device: context.device.toMessageObject(),
52
+ }));
53
+ }
54
+ throw error;
55
+ });
56
+ };
57
+ const validateState = async (context) => {
58
+ const { device, method } = context;
59
+ if (!method.useDeviceState) {
60
+ return;
61
+ }
62
+ const isDeviceUnlocked = device.features.unlocked;
63
+ const isUsingPopup = DataManager_1.DataManager.getSettings('popup');
64
+ try {
65
+ let invalidDeviceState = await getInvalidDeviceState(context);
66
+ if (isUsingPopup) {
67
+ while (invalidDeviceState) {
68
+ const uiPromise = method.createUiPromise(events_1.UI.INVALID_PASSPHRASE_ACTION, device);
69
+ method.postMessage((0, events_1.createUiMessage)(events_1.UI.INVALID_PASSPHRASE, {
70
+ device: device.toMessageObject(),
71
+ }));
72
+ const uiResp = await uiPromise.promise;
73
+ if (uiResp.payload) {
74
+ device.setState({ sessionId: undefined });
75
+ await device.initialize(method.useCardanoDerivation);
76
+ invalidDeviceState = await getInvalidDeviceState(context);
77
+ }
78
+ else {
79
+ device.setState({ staticSessionId: invalidDeviceState });
80
+ break;
81
+ }
82
+ }
83
+ }
84
+ else if (invalidDeviceState) {
85
+ throw constants_1.ERRORS.TypedError('Device_InvalidState');
86
+ }
87
+ }
88
+ catch (error) {
89
+ device.setState({ sessionId: undefined });
90
+ return Promise.reject(error);
91
+ }
92
+ if (!isDeviceUnlocked && device.features.unlocked) {
93
+ method.postMessage((0, events_1.createDeviceMessage)(events_1.DEVICE.CHANGED, device.toMessageObject()));
94
+ }
95
+ };
96
+ exports.validateState = validateState;
97
+ //# sourceMappingURL=validateState.js.map