@trezor/connect 9.4.8-beta.1 → 9.5.0-beta.1
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.
- package/CHANGELOG.md +33 -6
- package/README.md +1 -1
- package/lib/api/authorizeCoinjoin.js +1 -1
- package/lib/api/binance/api/binanceGetAddress.js +2 -4
- package/lib/api/binance/api/binanceGetPublicKey.js +1 -0
- package/lib/api/binance/api/binanceSignTransaction.js +1 -2
- package/lib/api/bitcoin/Fees.js +4 -1
- package/lib/api/bitcoin/TransactionComposer.js +9 -2
- package/lib/api/bitcoin/enhanceSignTx.js +1 -2
- package/lib/api/bitcoin/refTx.js +4 -6
- package/lib/api/blockchainDisconnect.js +1 -1
- package/lib/api/blockchainSubscribe.js +1 -2
- package/lib/api/blockchainUnsubscribe.js +1 -2
- package/lib/api/cardano/api/cardanoComposeTransaction.js +1 -1
- package/lib/api/cardano/api/cardanoGetAddress.js +2 -4
- package/lib/api/cardano/api/cardanoGetPublicKey.js +1 -0
- package/lib/api/cardano/cardanoAuxiliaryData.js +2 -2
- package/lib/api/cardano/cardanoUtils.js +5 -6
- package/lib/api/cipherKeyValue.js +1 -0
- package/lib/api/common/Discovery.js +16 -1
- package/lib/api/common/paramsValidator.js +2 -2
- package/lib/api/composeTransaction.js +6 -6
- package/lib/api/eos/api/eosGetPublicKey.js +1 -0
- package/lib/api/ethereum/api/ethereumGetAddress.js +2 -4
- package/lib/api/ethereum/api/ethereumGetPublicKey.js +1 -0
- package/lib/api/ethereum/api/ethereumSignMessage.js +1 -2
- package/lib/api/ethereum/api/ethereumSignTransaction.js +1 -1
- package/lib/api/ethereum/api/ethereumSignTypedData.js +1 -1
- package/lib/api/ethereum/ethereumDefinitions.js +2 -2
- package/lib/api/ethereum/ethereumSignTx.d.ts +2 -2
- package/lib/api/ethereum/ethereumSignTx.js +3 -2
- package/lib/api/firmware/verifyAuthenticityProof.js +3 -3
- package/lib/api/getAccountDescriptor.js +2 -4
- package/lib/api/getAccountInfo.js +4 -5
- package/lib/api/getAddress.js +5 -8
- package/lib/api/getDeviceState.js +1 -1
- package/lib/api/getOwnershipId.js +1 -0
- package/lib/api/getOwnershipProof.js +1 -0
- package/lib/api/getPublicKey.js +3 -2
- package/lib/api/nem/api/nemGetAddress.js +2 -4
- package/lib/api/nem/api/nemSignTransaction.js +1 -2
- package/lib/api/requestLogin.js +1 -0
- package/lib/api/ripple/api/rippleGetAddress.js +2 -4
- package/lib/api/signMessage.js +1 -2
- package/lib/api/signTransaction.js +6 -8
- package/lib/api/solana/additionalInfo.js +2 -3
- package/lib/api/solana/api/solanaComposeTransaction.js +2 -3
- package/lib/api/solana/api/solanaGetAddress.js +2 -4
- package/lib/api/solana/api/solanaGetPublicKey.js +1 -0
- package/lib/api/solana/solanaUtils.js +6 -5
- package/lib/api/stellar/api/stellarGetAddress.js +2 -4
- package/lib/api/tezos/api/tezosGetAddress.js +2 -4
- package/lib/api/tezos/api/tezosGetPublicKey.js +1 -0
- package/lib/backend/BackendManager.js +11 -16
- package/lib/backend/Blockchain.js +10 -4
- package/lib/backend/BlockchainLink.js +1 -1
- package/lib/constants/errors.js +2 -0
- package/lib/core/AbstractMethod.js +30 -5
- package/lib/core/index.js +24 -34
- package/lib/core/onCallFirmwareUpdate.d.ts +3 -2
- package/lib/core/onCallFirmwareUpdate.js +12 -59
- package/lib/data/DataManager.js +3 -2
- package/lib/data/analyticsInfo.js +10 -11
- package/lib/data/connectSettings.js +1 -2
- package/lib/data/defaultFeeLevels.js +4 -7
- package/lib/data/deviceAuthenticityConfig.js +16 -1
- package/lib/data/firmwareInfo.js +5 -6
- package/lib/data/version.d.ts +1 -1
- package/lib/data/version.js +1 -1
- package/lib/device/Device.d.ts +10 -2
- package/lib/device/Device.js +73 -56
- package/lib/device/DeviceCommands.d.ts +5 -1
- package/lib/device/DeviceCommands.js +7 -2
- package/lib/device/DeviceList.d.ts +2 -0
- package/lib/device/DeviceList.js +44 -24
- package/lib/device/StateStorage.js +5 -6
- package/lib/device/checkFirmwareRevision.js +11 -6
- package/lib/device/prompts.js +4 -7
- package/lib/events/call.d.ts +1 -0
- package/lib/events/call.js +3 -3
- package/lib/impl/core-in-module.js +25 -23
- package/lib/impl/dynamic.js +7 -1
- package/lib/index.js +9 -12
- package/lib/types/api/ethereum/index.d.ts +4 -4
- package/lib/types/api/ethereum/index.js +2 -2
- package/lib/types/api/firmwareUpdate.d.ts +1 -8
- package/lib/utils/assets.js +1 -1
- package/lib/utils/assets.native.js +1 -1
- package/lib/utils/hdnodeUtils.js +1 -2
- package/lib/utils/interactionTimeout.js +2 -2
- package/lib/utils/popupPromiseManager.js +2 -2
- package/lib/utils/proxy-event-emitter.js +1 -0
- package/lib/utils/uiPromiseManager.js +3 -4
- package/lib/utils/urlUtils.js +5 -8
- package/package.json +12 -12
- package/lib/utils/abortablePromise.d.ts +0 -5
- package/lib/utils/abortablePromise.js +0 -17
package/lib/device/Device.js
CHANGED
|
@@ -20,46 +20,68 @@ const types_1 = require("../types");
|
|
|
20
20
|
const debug_1 = require("../utils/debug");
|
|
21
21
|
const deviceFeaturesUtils_1 = require("../utils/deviceFeaturesUtils");
|
|
22
22
|
const _log = (0, debug_1.initLog)('Device');
|
|
23
|
-
exports.GET_FEATURES_TIMEOUT =
|
|
24
|
-
exports.GET_FEATURES_TIMEOUT_REACT_NATIVE =
|
|
23
|
+
exports.GET_FEATURES_TIMEOUT = 3_000;
|
|
24
|
+
exports.GET_FEATURES_TIMEOUT_REACT_NATIVE = 20_000;
|
|
25
25
|
const parseRunOptions = (options) => {
|
|
26
26
|
if (!options)
|
|
27
27
|
options = {};
|
|
28
28
|
return options;
|
|
29
29
|
};
|
|
30
30
|
class Device extends utils_1.TypedEmitter {
|
|
31
|
+
transport;
|
|
32
|
+
protocol;
|
|
33
|
+
transportPath;
|
|
34
|
+
transportSessionOwner;
|
|
35
|
+
transportDescriptorType;
|
|
36
|
+
session;
|
|
37
|
+
lastAcquiredHere;
|
|
38
|
+
unreadableError;
|
|
39
|
+
_firmwareStatus;
|
|
31
40
|
get firmwareStatus() {
|
|
32
41
|
return this._firmwareStatus;
|
|
33
42
|
}
|
|
43
|
+
_firmwareRelease;
|
|
34
44
|
get firmwareRelease() {
|
|
35
45
|
return this._firmwareRelease;
|
|
36
46
|
}
|
|
47
|
+
_features;
|
|
37
48
|
get features() {
|
|
38
49
|
return this._features;
|
|
39
50
|
}
|
|
51
|
+
_featuresNeedsReload = false;
|
|
52
|
+
acquirePromise;
|
|
53
|
+
releasePromise;
|
|
54
|
+
runPromise;
|
|
55
|
+
keepTransportSession = false;
|
|
56
|
+
commands;
|
|
57
|
+
cancelableAction;
|
|
58
|
+
loaded = false;
|
|
59
|
+
inconsistent = false;
|
|
60
|
+
firstRunPromise;
|
|
61
|
+
instance = 0;
|
|
62
|
+
state = [];
|
|
63
|
+
stateStorage = undefined;
|
|
64
|
+
_unavailableCapabilities = {};
|
|
40
65
|
get unavailableCapabilities() {
|
|
41
66
|
return this._unavailableCapabilities;
|
|
42
67
|
}
|
|
68
|
+
_firmwareType;
|
|
43
69
|
get firmwareType() {
|
|
44
70
|
return this._firmwareType;
|
|
45
71
|
}
|
|
72
|
+
name = 'Trezor';
|
|
73
|
+
color;
|
|
74
|
+
availableTranslations = [];
|
|
75
|
+
authenticityChecks = {
|
|
76
|
+
firmwareRevision: null,
|
|
77
|
+
firmwareHash: null,
|
|
78
|
+
};
|
|
79
|
+
uniquePath;
|
|
80
|
+
emitLifecycle;
|
|
81
|
+
sessionDfd;
|
|
82
|
+
handshakeFinished = false;
|
|
46
83
|
constructor({ id, transport, descriptor, listener }) {
|
|
47
84
|
super();
|
|
48
|
-
this._featuresNeedsReload = false;
|
|
49
|
-
this.keepTransportSession = false;
|
|
50
|
-
this.loaded = false;
|
|
51
|
-
this.inconsistent = false;
|
|
52
|
-
this.instance = 0;
|
|
53
|
-
this.state = [];
|
|
54
|
-
this.stateStorage = undefined;
|
|
55
|
-
this._unavailableCapabilities = {};
|
|
56
|
-
this.name = 'Trezor';
|
|
57
|
-
this.availableTranslations = [];
|
|
58
|
-
this.authenticityChecks = {
|
|
59
|
-
firmwareRevision: null,
|
|
60
|
-
firmwareHash: null,
|
|
61
|
-
};
|
|
62
|
-
this.handshakeFinished = false;
|
|
63
85
|
this.emitLifecycle = listener;
|
|
64
86
|
this.protocol = protocol_1.v1;
|
|
65
87
|
this.uniquePath = id;
|
|
@@ -107,11 +129,10 @@ class Device extends utils_1.TypedEmitter {
|
|
|
107
129
|
.acquire({ input: { path: this.transportPath, previous: this.session } })
|
|
108
130
|
.then(result => this.waitAndCompareSession(result, sessionPromise))
|
|
109
131
|
.then(result => {
|
|
110
|
-
var _a;
|
|
111
132
|
if (result.success) {
|
|
112
133
|
this.session = result.payload;
|
|
113
134
|
this.lastAcquiredHere = true;
|
|
114
|
-
|
|
135
|
+
this.commands?.dispose();
|
|
115
136
|
this.commands = new DeviceCommands_1.DeviceCommands(this, this.transport, this.session);
|
|
116
137
|
return result;
|
|
117
138
|
}
|
|
@@ -165,9 +186,8 @@ class Device extends utils_1.TypedEmitter {
|
|
|
165
186
|
}
|
|
166
187
|
}
|
|
167
188
|
async handshake(delay) {
|
|
168
|
-
var _a;
|
|
169
189
|
if (delay) {
|
|
170
|
-
await (0, utils_1.
|
|
190
|
+
await (0, utils_1.resolveAfter)(501 + delay);
|
|
171
191
|
}
|
|
172
192
|
while (true) {
|
|
173
193
|
if (this.isUsedElsewhere()) {
|
|
@@ -192,12 +212,12 @@ class Device extends utils_1.TypedEmitter {
|
|
|
192
212
|
this.emitLifecycle(events_1.DEVICE.CONNECT_UNACQUIRED);
|
|
193
213
|
}
|
|
194
214
|
else if (error.message === transport_1.TRANSPORT_ERROR.INTERFACE_UNABLE_TO_OPEN_DEVICE ||
|
|
195
|
-
|
|
196
|
-
this.unreadableError = error
|
|
215
|
+
error.message?.indexOf(constants_1.ERRORS.LIBUSB_ERROR_MESSAGE) >= 0) {
|
|
216
|
+
this.unreadableError = error?.message;
|
|
197
217
|
this.emitLifecycle(events_1.DEVICE.CONNECT_UNACQUIRED);
|
|
198
218
|
}
|
|
199
219
|
else {
|
|
200
|
-
await (0, utils_1.
|
|
220
|
+
await (0, utils_1.resolveAfter)(501);
|
|
201
221
|
continue;
|
|
202
222
|
}
|
|
203
223
|
}
|
|
@@ -207,14 +227,13 @@ class Device extends utils_1.TypedEmitter {
|
|
|
207
227
|
}
|
|
208
228
|
}
|
|
209
229
|
async updateDescriptor(descriptor) {
|
|
210
|
-
|
|
211
|
-
(_a = this.sessionDfd) === null || _a === void 0 ? void 0 : _a.resolve(descriptor.session);
|
|
230
|
+
this.sessionDfd?.resolve(descriptor.session);
|
|
212
231
|
await Promise.all([this.acquirePromise, this.releasePromise]);
|
|
213
232
|
if (descriptor.session && descriptor.session !== this.session) {
|
|
214
233
|
this.usedElsewhere();
|
|
215
234
|
}
|
|
216
235
|
if (!descriptor.session) {
|
|
217
|
-
const methodStillRunning = !
|
|
236
|
+
const methodStillRunning = !this.commands?.isDisposed();
|
|
218
237
|
if (methodStillRunning) {
|
|
219
238
|
this.releaseTransportSession();
|
|
220
239
|
}
|
|
@@ -266,10 +285,9 @@ class Device extends utils_1.TypedEmitter {
|
|
|
266
285
|
this.cancelableAction = undefined;
|
|
267
286
|
}
|
|
268
287
|
async interruptionFromUser(error) {
|
|
269
|
-
var _a, _b;
|
|
270
288
|
_log.debug('interruptionFromUser');
|
|
271
|
-
await
|
|
272
|
-
await
|
|
289
|
+
await this.cancelableAction?.(error);
|
|
290
|
+
await this.commands?.cancel();
|
|
273
291
|
if (this.runPromise) {
|
|
274
292
|
this.runPromise.reject(error);
|
|
275
293
|
delete this.runPromise;
|
|
@@ -294,11 +312,10 @@ class Device extends utils_1.TypedEmitter {
|
|
|
294
312
|
}
|
|
295
313
|
}
|
|
296
314
|
async _runInner(fn, options) {
|
|
297
|
-
var _a, _b;
|
|
298
315
|
if (this.releasePromise) {
|
|
299
316
|
await this.releasePromise;
|
|
300
317
|
}
|
|
301
|
-
const acquireNeeded = !this.isUsedHere() ||
|
|
318
|
+
const acquireNeeded = !this.isUsedHere() || this.commands?.disposed;
|
|
302
319
|
if (acquireNeeded) {
|
|
303
320
|
await this.acquire();
|
|
304
321
|
}
|
|
@@ -312,6 +329,14 @@ class Device extends utils_1.TypedEmitter {
|
|
|
312
329
|
const getFeaturesTimeout = DataManager_1.DataManager.getSettings('env') === 'react-native'
|
|
313
330
|
? exports.GET_FEATURES_TIMEOUT_REACT_NATIVE
|
|
314
331
|
: exports.GET_FEATURES_TIMEOUT;
|
|
332
|
+
if (['v1', 'bridge'].includes(this.protocol.name)) {
|
|
333
|
+
_log.debug('sending a preventive cancel on the first encounter with the device');
|
|
334
|
+
try {
|
|
335
|
+
await this.getCommands().typedCall('Cancel', 'Failure', {});
|
|
336
|
+
}
|
|
337
|
+
catch {
|
|
338
|
+
}
|
|
339
|
+
}
|
|
315
340
|
let getFeaturesTimeoutId;
|
|
316
341
|
await Promise.race([
|
|
317
342
|
this.getFeatures().finally(() => clearTimeout(getFeaturesTimeoutId)),
|
|
@@ -345,7 +370,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
345
370
|
await this.checkFirmwareRevisionWithRetries();
|
|
346
371
|
}
|
|
347
372
|
if (!options.skipLanguageChecks &&
|
|
348
|
-
|
|
373
|
+
this.features?.language &&
|
|
349
374
|
!this.features.language_version_matches &&
|
|
350
375
|
this.atLeast('2.7.0')) {
|
|
351
376
|
_log.info('language version mismatch. silently updating...');
|
|
@@ -401,7 +426,6 @@ class Device extends utils_1.TypedEmitter {
|
|
|
401
426
|
return this.state[this.instance];
|
|
402
427
|
}
|
|
403
428
|
setState(state) {
|
|
404
|
-
var _a;
|
|
405
429
|
if (!state) {
|
|
406
430
|
delete this.state[this.instance];
|
|
407
431
|
}
|
|
@@ -412,11 +436,10 @@ class Device extends utils_1.TypedEmitter {
|
|
|
412
436
|
...state,
|
|
413
437
|
};
|
|
414
438
|
this.state[this.instance] = newState;
|
|
415
|
-
|
|
439
|
+
this.stateStorage?.saveState(this, newState);
|
|
416
440
|
}
|
|
417
441
|
}
|
|
418
442
|
async validateState(preauthorized = false) {
|
|
419
|
-
var _a;
|
|
420
443
|
if (!this.features)
|
|
421
444
|
return;
|
|
422
445
|
if (!this.features.unlocked && preauthorized) {
|
|
@@ -424,7 +447,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
424
447
|
return;
|
|
425
448
|
}
|
|
426
449
|
}
|
|
427
|
-
const expectedState =
|
|
450
|
+
const expectedState = this.getState()?.staticSessionId;
|
|
428
451
|
const state = await this.getCommands().getDeviceState();
|
|
429
452
|
const uniqueState = `${state}@${this.features.device_id}:${this.instance}`;
|
|
430
453
|
if (this.features.session_id) {
|
|
@@ -450,7 +473,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
450
473
|
}
|
|
451
474
|
const { message } = await this.getCommands().typedCall('Initialize', 'Features', payload);
|
|
452
475
|
this._updateFeatures(message);
|
|
453
|
-
this.setState({ deriveCardano: payload
|
|
476
|
+
this.setState({ deriveCardano: payload?.derive_cardano });
|
|
454
477
|
}
|
|
455
478
|
initStorage(storage) {
|
|
456
479
|
this.stateStorage = storage;
|
|
@@ -461,15 +484,14 @@ class Device extends utils_1.TypedEmitter {
|
|
|
461
484
|
this._updateFeatures(message);
|
|
462
485
|
}
|
|
463
486
|
async checkFirmwareHashWithRetries() {
|
|
464
|
-
var _a;
|
|
465
487
|
const lastResult = this.authenticityChecks.firmwareHash;
|
|
466
488
|
const notDoneYet = lastResult === null;
|
|
467
|
-
const attemptsDone =
|
|
489
|
+
const attemptsDone = lastResult?.attemptCount ?? 0;
|
|
468
490
|
if (attemptsDone >= constants_1.FIRMWARE.HASH_CHECK_MAX_ATTEMPTS)
|
|
469
491
|
return;
|
|
470
492
|
const wasError = lastResult !== null && !lastResult.success;
|
|
471
493
|
const wasErrorRetriable = wasError && (0, utils_1.isArrayMember)(lastResult.error, constants_1.FIRMWARE.HASH_CHECK_RETRIABLE_ERRORS);
|
|
472
|
-
const lastErrorPayload = wasError ? lastResult
|
|
494
|
+
const lastErrorPayload = wasError ? lastResult?.errorPayload : null;
|
|
473
495
|
if (notDoneYet || wasErrorRetriable) {
|
|
474
496
|
const result = await this.checkFirmwareHash();
|
|
475
497
|
this.authenticityChecks.firmwareHash = result;
|
|
@@ -482,7 +504,6 @@ class Device extends utils_1.TypedEmitter {
|
|
|
482
504
|
}
|
|
483
505
|
}
|
|
484
506
|
async checkFirmwareHash() {
|
|
485
|
-
var _a;
|
|
486
507
|
const createFailResult = (error, errorPayload) => ({
|
|
487
508
|
success: false,
|
|
488
509
|
error,
|
|
@@ -515,7 +536,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
515
536
|
const { hash: expectedHash, challenge } = (0, firmware_1.calculateFirmwareHash)(this.features.major_version, strippedBinary, (0, crypto_1.randomBytes)(32));
|
|
516
537
|
try {
|
|
517
538
|
const deviceResponse = await this.getCommands().typedCall('GetFirmwareHash', 'FirmwareHash', { challenge });
|
|
518
|
-
if (!
|
|
539
|
+
if (!deviceResponse?.message?.hash) {
|
|
519
540
|
return createFailResult('other-error', 'Device response is missing hash');
|
|
520
541
|
}
|
|
521
542
|
if (deviceResponse.message.hash !== expectedHash) {
|
|
@@ -552,7 +573,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
552
573
|
internalModel: this.features.internal_model,
|
|
553
574
|
deviceRevision: this.features.revision,
|
|
554
575
|
firmwareVersion,
|
|
555
|
-
expectedRevision: release
|
|
576
|
+
expectedRevision: release?.firmware_revision,
|
|
556
577
|
});
|
|
557
578
|
}
|
|
558
579
|
async changeLanguage({ language, binary, }) {
|
|
@@ -597,13 +618,12 @@ class Device extends utils_1.TypedEmitter {
|
|
|
597
618
|
return response.message;
|
|
598
619
|
}
|
|
599
620
|
_updateFeatures(feat) {
|
|
600
|
-
var _a, _b, _c, _d;
|
|
601
621
|
const capabilities = (0, deviceFeaturesUtils_1.parseCapabilities)(feat);
|
|
602
622
|
feat.capabilities = capabilities;
|
|
603
623
|
if (this.features && this.features.session_id && !feat.session_id) {
|
|
604
624
|
feat.session_id = this.features.session_id;
|
|
605
625
|
}
|
|
606
|
-
feat.unlocked =
|
|
626
|
+
feat.unlocked = feat.unlocked ?? true;
|
|
607
627
|
const revision = (0, deviceFeaturesUtils_1.parseRevision)(feat);
|
|
608
628
|
feat.revision = revision;
|
|
609
629
|
if (!feat.model && feat.major_version === 1) {
|
|
@@ -629,7 +649,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
629
649
|
this._unavailableCapabilities = (0, deviceFeaturesUtils_1.getUnavailableCapabilities)(feat, (0, coinInfo_1.getAllNetworks)());
|
|
630
650
|
this._firmwareStatus = (0, firmwareInfo_1.getFirmwareStatus)(feat);
|
|
631
651
|
this._firmwareRelease = (0, firmwareInfo_1.getRelease)(feat);
|
|
632
|
-
this.availableTranslations =
|
|
652
|
+
this.availableTranslations = this.firmwareRelease?.translations ?? [];
|
|
633
653
|
}
|
|
634
654
|
this._features = feat;
|
|
635
655
|
this._featuresNeedsReload = false;
|
|
@@ -647,12 +667,12 @@ class Device extends utils_1.TypedEmitter {
|
|
|
647
667
|
? types_1.FirmwareType.BitcoinOnly
|
|
648
668
|
: types_1.FirmwareType.Regular;
|
|
649
669
|
}
|
|
650
|
-
const deviceInfo =
|
|
670
|
+
const deviceInfo = models_1.models[feat.internal_model] ?? {
|
|
651
671
|
name: `Unknown ${feat.internal_model}`,
|
|
652
672
|
colors: {},
|
|
653
673
|
};
|
|
654
674
|
this.name = deviceInfo.name;
|
|
655
|
-
if (feat
|
|
675
|
+
if (feat?.unit_color) {
|
|
656
676
|
const deviceUnitColor = feat.unit_color.toString();
|
|
657
677
|
if (deviceUnitColor in deviceInfo.colors) {
|
|
658
678
|
this.color = deviceInfo.colors[deviceUnitColor];
|
|
@@ -666,9 +686,8 @@ class Device extends utils_1.TypedEmitter {
|
|
|
666
686
|
return !!this.unreadableError;
|
|
667
687
|
}
|
|
668
688
|
disconnect() {
|
|
669
|
-
var _a;
|
|
670
689
|
_log.debug('Disconnect cleanup');
|
|
671
|
-
|
|
690
|
+
this.sessionDfd?.reject(new Error());
|
|
672
691
|
this.lastAcquiredHere = false;
|
|
673
692
|
this.emitLifecycle(events_1.DEVICE.DISCONNECT);
|
|
674
693
|
return this.interruptionFromUser(constants_1.ERRORS.TypedError('Device_Disconnected'));
|
|
@@ -746,12 +765,11 @@ class Device extends utils_1.TypedEmitter {
|
|
|
746
765
|
return null;
|
|
747
766
|
}
|
|
748
767
|
async dispose() {
|
|
749
|
-
var _a, _b;
|
|
750
768
|
this.removeAllListeners();
|
|
751
769
|
if (this.session && this.lastAcquiredHere) {
|
|
752
770
|
try {
|
|
753
|
-
await
|
|
754
|
-
await
|
|
771
|
+
await this.cancelableAction?.();
|
|
772
|
+
await this.commands?.cancel();
|
|
755
773
|
return this.transport.release({
|
|
756
774
|
session: this.session,
|
|
757
775
|
path: this.transportPath,
|
|
@@ -772,7 +790,6 @@ class Device extends utils_1.TypedEmitter {
|
|
|
772
790
|
return 'normal';
|
|
773
791
|
}
|
|
774
792
|
toMessageObject() {
|
|
775
|
-
var _a;
|
|
776
793
|
const { name, uniquePath: path } = this;
|
|
777
794
|
const base = { path, name };
|
|
778
795
|
if (this.unreadableError) {
|
|
@@ -804,7 +821,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
804
821
|
id: this.features.device_id,
|
|
805
822
|
label,
|
|
806
823
|
_state: this.getState(),
|
|
807
|
-
state:
|
|
824
|
+
state: this.getState()?.staticSessionId,
|
|
808
825
|
status,
|
|
809
826
|
mode: this.getMode(),
|
|
810
827
|
color: this.color,
|
|
@@ -80,7 +80,11 @@ export declare class DeviceCommands {
|
|
|
80
80
|
cancelWithFallback(): Promise<{
|
|
81
81
|
readonly success: false;
|
|
82
82
|
readonly error: "session not found";
|
|
83
|
-
} | import("@trezor/transport/lib/types").Success<import("@trezor/protobuf").MessageFromTrezor> |
|
|
83
|
+
} | import("@trezor/transport/lib/types").Success<import("@trezor/protobuf").MessageFromTrezor> | {
|
|
84
|
+
success: false;
|
|
85
|
+
error: "session not found" | "Network request failed" | "Wrong result type." | "other call in progress" | "Malformed protocol format" | "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" | "A transfer error has occurred.";
|
|
86
|
+
message?: string;
|
|
87
|
+
} | import("@trezor/transport/lib/types").Success<undefined> | undefined>;
|
|
84
88
|
cancel(): Promise<void>;
|
|
85
89
|
}
|
|
86
90
|
export type TypedCall = DeviceCommands['typedCall'];
|
|
@@ -51,6 +51,11 @@ const filterForLog = (type, msg) => {
|
|
|
51
51
|
return msg;
|
|
52
52
|
};
|
|
53
53
|
class DeviceCommands {
|
|
54
|
+
device;
|
|
55
|
+
transport;
|
|
56
|
+
transportSession;
|
|
57
|
+
disposed;
|
|
58
|
+
callPromise;
|
|
54
59
|
constructor(device, transport, transportSession) {
|
|
55
60
|
this.device = device;
|
|
56
61
|
this.transport = transport;
|
|
@@ -234,7 +239,7 @@ class DeviceCommands {
|
|
|
234
239
|
if (this.disposed) {
|
|
235
240
|
throw constants_1.ERRORS.TypedError('Runtime', 'typedCall: DeviceCommands already disposed');
|
|
236
241
|
}
|
|
237
|
-
(0, schema_utils_1.Assert)(protobuf_1.MessagesSchema.MessageType.properties[type], msg
|
|
242
|
+
(0, schema_utils_1.Assert)(protobuf_1.MessagesSchema.MessageType.properties[type], msg ?? {});
|
|
238
243
|
const response = await this._commonCall(type, msg);
|
|
239
244
|
try {
|
|
240
245
|
assertType(response, resType);
|
|
@@ -372,7 +377,7 @@ class DeviceCommands {
|
|
|
372
377
|
const { name, version } = this.transport;
|
|
373
378
|
if (name === 'BridgeTransport' && !utils_1.versionUtils.isNewer(version, '2.0.28')) {
|
|
374
379
|
try {
|
|
375
|
-
await (0, utils_1.
|
|
380
|
+
await (0, utils_1.resolveAfter)(1);
|
|
376
381
|
await this.device.acquire();
|
|
377
382
|
await (0, prompts_1.cancelPrompt)(this.device, false);
|
|
378
383
|
}
|
|
@@ -45,6 +45,7 @@ export declare class DeviceList extends TypedEmitter<DeviceListEvents> implement
|
|
|
45
45
|
}[];
|
|
46
46
|
private readonly locks;
|
|
47
47
|
private transportLock;
|
|
48
|
+
private readonly scheduledUpgradeChecks;
|
|
48
49
|
constructor({ messages, priority, debug, _sessionsBackgroundUrl, manifest, }: ConstructorParams);
|
|
49
50
|
private tryGetTransport;
|
|
50
51
|
private getOrCreateTransport;
|
|
@@ -56,6 +57,7 @@ export declare class DeviceList extends TypedEmitter<DeviceListEvents> implement
|
|
|
56
57
|
init(initParams?: InitParams): Promise<void>;
|
|
57
58
|
private createInitPromise;
|
|
58
59
|
private createReconnectDelay;
|
|
60
|
+
private scheduleUpgradeCheck;
|
|
59
61
|
private selectTransport;
|
|
60
62
|
private initializeTransport;
|
|
61
63
|
private waitForDevices;
|
package/lib/device/DeviceList.js
CHANGED
|
@@ -9,7 +9,6 @@ const Device_1 = require("./Device");
|
|
|
9
9
|
const transportInfo_1 = require("../data/transportInfo");
|
|
10
10
|
const types_1 = require("../types");
|
|
11
11
|
const utils_2 = require("../types/utils");
|
|
12
|
-
const abortablePromise_1 = require("../utils/abortablePromise");
|
|
13
12
|
const debug_1 = require("../utils/debug");
|
|
14
13
|
const createAuthPenaltyManager = (priority) => {
|
|
15
14
|
const penalizedDevices = {};
|
|
@@ -68,6 +67,13 @@ const assertDeviceListConnected = deviceList => {
|
|
|
68
67
|
};
|
|
69
68
|
exports.assertDeviceListConnected = assertDeviceListConnected;
|
|
70
69
|
class DeviceList extends utils_1.TypedEmitter {
|
|
70
|
+
transport = {};
|
|
71
|
+
transports = [];
|
|
72
|
+
devices = createDeviceCollection();
|
|
73
|
+
deviceCounter = Date.now();
|
|
74
|
+
handshakeLock;
|
|
75
|
+
authPenaltyManager;
|
|
76
|
+
transportCommonArgs;
|
|
71
77
|
isConnected() {
|
|
72
78
|
return !!Object.keys(this.transport).length;
|
|
73
79
|
}
|
|
@@ -81,13 +87,13 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
81
87
|
getActiveTransports() {
|
|
82
88
|
return Object.values(this.transport).map(getTransportInfo);
|
|
83
89
|
}
|
|
90
|
+
locks = {};
|
|
84
91
|
async transportLock(apiType, abortMessage, action) {
|
|
85
|
-
|
|
86
|
-
const lock = (_a = this.locks[apiType]) !== null && _a !== void 0 ? _a : (this.locks[apiType] = { sequence: 0 });
|
|
92
|
+
const lock = this.locks[apiType] ?? (this.locks[apiType] = { sequence: 0 });
|
|
87
93
|
lock.abortMessage = abortMessage;
|
|
88
94
|
const sequence = ++lock.sequence;
|
|
89
95
|
while (lock.promise) {
|
|
90
|
-
|
|
96
|
+
lock.abort?.abort(new Error(abortMessage));
|
|
91
97
|
await lock.promise.catch(() => { });
|
|
92
98
|
}
|
|
93
99
|
if (sequence !== lock.sequence)
|
|
@@ -99,13 +105,9 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
99
105
|
});
|
|
100
106
|
return lock.promise;
|
|
101
107
|
}
|
|
108
|
+
scheduledUpgradeChecks = {};
|
|
102
109
|
constructor({ messages, priority, debug, _sessionsBackgroundUrl, manifest, }) {
|
|
103
110
|
super();
|
|
104
|
-
this.transport = {};
|
|
105
|
-
this.transports = [];
|
|
106
|
-
this.devices = createDeviceCollection();
|
|
107
|
-
this.deviceCounter = Date.now();
|
|
108
|
-
this.locks = {};
|
|
109
111
|
const transportLogger = (0, debug_1.initLog)('@trezor/transport', debug);
|
|
110
112
|
this.handshakeLock = (0, utils_1.getSynchronize)();
|
|
111
113
|
this.authPenaltyManager = createAuthPenaltyManager(priority);
|
|
@@ -113,14 +115,13 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
113
115
|
messages,
|
|
114
116
|
logger: transportLogger,
|
|
115
117
|
sessionsBackgroundUrl: _sessionsBackgroundUrl,
|
|
116
|
-
id:
|
|
118
|
+
id: manifest?.appUrl || 'unknown app',
|
|
117
119
|
};
|
|
118
120
|
}
|
|
119
121
|
tryGetTransport(name) {
|
|
120
122
|
return this.transports.find(t => t.name === name);
|
|
121
123
|
}
|
|
122
124
|
getOrCreateTransport(transportType) {
|
|
123
|
-
var _a;
|
|
124
125
|
const { transportCommonArgs } = this;
|
|
125
126
|
if (typeof transportType === 'string') {
|
|
126
127
|
const existing = this.tryGetTransport(transportType);
|
|
@@ -143,7 +144,7 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
143
144
|
else if (typeof transportType === 'function' && 'prototype' in transportType) {
|
|
144
145
|
const transportInstance = new transportType(transportCommonArgs);
|
|
145
146
|
if ((0, transport_1.isTransportInstance)(transportInstance)) {
|
|
146
|
-
return
|
|
147
|
+
return this.tryGetTransport(transportInstance.name) ?? transportInstance;
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
else if ((0, transport_1.isTransportInstance)(transportType)) {
|
|
@@ -158,7 +159,7 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
158
159
|
throw constants_1.ERRORS.TypedError('Runtime', `DeviceList.init: transports[] of unexpected type: ${transportType}`);
|
|
159
160
|
}
|
|
160
161
|
createTransports(transports) {
|
|
161
|
-
const transportTypes =
|
|
162
|
+
const transportTypes = transports?.length ? transports : ['BridgeTransport'];
|
|
162
163
|
return transportTypes.map(this.getOrCreateTransport.bind(this));
|
|
163
164
|
}
|
|
164
165
|
onDeviceConnected(descriptor, transport) {
|
|
@@ -179,15 +180,15 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
179
180
|
}
|
|
180
181
|
onDeviceDisconnected(descriptor, transport) {
|
|
181
182
|
const device = this.devices.remove(descriptor.path, transport);
|
|
182
|
-
device
|
|
183
|
+
device?.disconnect();
|
|
183
184
|
}
|
|
184
185
|
onDeviceSessionChanged(descriptor, transport) {
|
|
185
186
|
const device = this.devices.get(descriptor.path, transport);
|
|
186
|
-
device
|
|
187
|
+
device?.updateDescriptor(descriptor);
|
|
187
188
|
}
|
|
188
189
|
onDeviceRequestRelease(descriptor, transport) {
|
|
189
190
|
const device = this.devices.get(descriptor.path, transport);
|
|
190
|
-
device
|
|
191
|
+
device?.usedElsewhere();
|
|
191
192
|
}
|
|
192
193
|
async init(initParams = {}) {
|
|
193
194
|
this.transports = this.createTransports(initParams.transports);
|
|
@@ -237,9 +238,12 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
237
238
|
this.emit(transport_1.TRANSPORT.START, getTransportInfo(transport));
|
|
238
239
|
}
|
|
239
240
|
}
|
|
241
|
+
if (transport && transport !== transports[0]) {
|
|
242
|
+
this.scheduleUpgradeCheck(apiType, initParams);
|
|
243
|
+
}
|
|
240
244
|
}
|
|
241
245
|
catch (error) {
|
|
242
|
-
this.emit(transport_1.TRANSPORT.ERROR, { apiType, error: error
|
|
246
|
+
this.emit(transport_1.TRANSPORT.ERROR, { apiType, error: error?.message });
|
|
243
247
|
if (initParams.transportReconnect && !abortSignal.aborted) {
|
|
244
248
|
this.transportLock(apiType, 'Reconnecting', async (signal) => {
|
|
245
249
|
await this.createReconnectDelay(signal);
|
|
@@ -249,9 +253,25 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
249
253
|
}
|
|
250
254
|
}
|
|
251
255
|
createReconnectDelay(signal) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
256
|
+
return (0, utils_1.resolveAfter)(1000, signal);
|
|
257
|
+
}
|
|
258
|
+
scheduleUpgradeCheck(apiType, initParams) {
|
|
259
|
+
clearTimeout(this.scheduledUpgradeChecks[apiType]);
|
|
260
|
+
this.scheduledUpgradeChecks[apiType] = setTimeout(async () => {
|
|
261
|
+
const transport = this.transport[apiType];
|
|
262
|
+
const transports = this.transports.filter(t => t.apiType === apiType);
|
|
263
|
+
if (!transport || transport === transports[0])
|
|
264
|
+
return;
|
|
265
|
+
for (const t of transports) {
|
|
266
|
+
if (t === transport)
|
|
267
|
+
break;
|
|
268
|
+
if (await t.ping()) {
|
|
269
|
+
this.transportLock(apiType, 'Upgrading', signal => this.createInitPromise(apiType, initParams, signal)).catch(() => { });
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
this.scheduleUpgradeCheck(apiType, initParams);
|
|
274
|
+
}, 1000);
|
|
255
275
|
}
|
|
256
276
|
async selectTransport([transport, ...rest], signal) {
|
|
257
277
|
if (signal.aborted)
|
|
@@ -324,7 +344,7 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
324
344
|
}
|
|
325
345
|
getDeviceByStaticState(state) {
|
|
326
346
|
const deviceId = state.split('@')[1].split(':')[0];
|
|
327
|
-
return this.devices.all().find(d =>
|
|
347
|
+
return this.devices.all().find(d => d.features?.device_id === deviceId);
|
|
328
348
|
}
|
|
329
349
|
async dispose() {
|
|
330
350
|
this.removeAllListeners();
|
|
@@ -341,6 +361,7 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
341
361
|
await Promise.all(promises);
|
|
342
362
|
}
|
|
343
363
|
async stopTransport(transport) {
|
|
364
|
+
clearTimeout(this.scheduledUpgradeChecks[transport.apiType]);
|
|
344
365
|
const devices = this.devices.clear(transport);
|
|
345
366
|
devices.forEach(device => {
|
|
346
367
|
this.emit(events_1.DEVICE.DISCONNECT, device);
|
|
@@ -349,7 +370,7 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
349
370
|
this.authPenaltyManager.remove(device);
|
|
350
371
|
await device.dispose();
|
|
351
372
|
}));
|
|
352
|
-
transport
|
|
373
|
+
transport?.stop();
|
|
353
374
|
}
|
|
354
375
|
async enumerate() {
|
|
355
376
|
const promises = Object.values(this.transport).map(async (transport) => {
|
|
@@ -358,8 +379,7 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
358
379
|
return;
|
|
359
380
|
}
|
|
360
381
|
res.payload.forEach(d => {
|
|
361
|
-
|
|
362
|
-
(_a = this.devices.get(d.path, transport)) === null || _a === void 0 ? void 0 : _a.updateDescriptor(d);
|
|
382
|
+
this.devices.get(d.path, transport)?.updateDescriptor(d);
|
|
363
383
|
});
|
|
364
384
|
});
|
|
365
385
|
await Promise.all(promises);
|
|
@@ -3,15 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.WebextensionStateStorage = void 0;
|
|
4
4
|
const connect_common_1 = require("@trezor/connect-common");
|
|
5
5
|
class WebextensionStateStorage {
|
|
6
|
+
origin;
|
|
6
7
|
constructor(origin) {
|
|
7
8
|
this.origin = origin;
|
|
8
9
|
}
|
|
9
10
|
loadState(device) {
|
|
10
|
-
|
|
11
|
-
if (!((_a = device.getState()) === null || _a === void 0 ? void 0 : _a.sessionId)) {
|
|
11
|
+
if (!device.getState()?.sessionId) {
|
|
12
12
|
const { preferredDevice } = connect_common_1.storage.loadForOrigin(this.origin) || {};
|
|
13
|
-
if (
|
|
14
|
-
|
|
13
|
+
if (preferredDevice?.internalState &&
|
|
14
|
+
preferredDevice?.internalStateExpiration &&
|
|
15
15
|
preferredDevice.internalStateExpiration > new Date().getTime()) {
|
|
16
16
|
return { sessionId: preferredDevice.internalState };
|
|
17
17
|
}
|
|
@@ -19,8 +19,7 @@ class WebextensionStateStorage {
|
|
|
19
19
|
return undefined;
|
|
20
20
|
}
|
|
21
21
|
saveState(device, state) {
|
|
22
|
-
|
|
23
|
-
const expirationDelay = Math.max(1000 * 60 * 15, (_a = device.features.auto_lock_delay_ms) !== null && _a !== void 0 ? _a : 0);
|
|
22
|
+
const expirationDelay = Math.max(1000 * 60 * 15, device.features.auto_lock_delay_ms ?? 0);
|
|
24
23
|
connect_common_1.storage.saveForOrigin(store => ({
|
|
25
24
|
...store,
|
|
26
25
|
preferredDevice: store.preferredDevice
|
|
@@ -4,7 +4,13 @@ exports.checkFirmwareRevision = void 0;
|
|
|
4
4
|
const versionUtils_1 = require("@trezor/utils/lib/versionUtils");
|
|
5
5
|
const downloadReleasesMetadata_1 = require("../data/downloadReleasesMetadata");
|
|
6
6
|
const calculateRevisionForDevice_1 = require("./calculateRevisionForDevice");
|
|
7
|
-
const
|
|
7
|
+
const isNodeJSNetworkError = (e) => ['FetchError', 'AbortError'].includes(e.name);
|
|
8
|
+
const isReactNativeNetworkError = (e) => e.name === 'TypeError' && e.message.includes('Network request failed');
|
|
9
|
+
const isNetworkError = (e) => {
|
|
10
|
+
if (!(e instanceof Error))
|
|
11
|
+
return false;
|
|
12
|
+
return isNodeJSNetworkError(e) || isReactNativeNetworkError(e);
|
|
13
|
+
};
|
|
8
14
|
const getOnlineReleaseMetadata = async ({ firmwareVersion, internalModel, }) => {
|
|
9
15
|
const onlineReleases = await (0, downloadReleasesMetadata_1.downloadReleasesMetadata)({ internal_model: internalModel });
|
|
10
16
|
return onlineReleases.find(onlineRelease => (0, versionUtils_1.isEqual)(onlineRelease.version, firmwareVersion));
|
|
@@ -30,7 +36,7 @@ const checkFirmwareRevision = async ({ firmwareVersion, internalModel, deviceRev
|
|
|
30
36
|
firmwareVersion,
|
|
31
37
|
internalModel,
|
|
32
38
|
});
|
|
33
|
-
if (
|
|
39
|
+
if (onlineRelease?.firmware_revision === undefined) {
|
|
34
40
|
return failFirmwareRevisionCheck('firmware-version-unknown');
|
|
35
41
|
}
|
|
36
42
|
if (!doRevisionsMatch({
|
|
@@ -43,10 +49,9 @@ const checkFirmwareRevision = async ({ firmwareVersion, internalModel, deviceRev
|
|
|
43
49
|
return { success: true };
|
|
44
50
|
}
|
|
45
51
|
catch (e) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return failFirmwareRevisionCheck('other-error');
|
|
52
|
+
return isNetworkError(e)
|
|
53
|
+
? failFirmwareRevisionCheck('cannot-perform-check-offline')
|
|
54
|
+
: failFirmwareRevisionCheck('other-error');
|
|
50
55
|
}
|
|
51
56
|
}
|
|
52
57
|
if (!doRevisionsMatch({
|