@trezor/connect 9.4.8-beta.2 → 9.5.0
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 +59 -44
- 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 +3 -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 +65 -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 +14 -14
- 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
|
}
|
|
@@ -353,7 +370,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
353
370
|
await this.checkFirmwareRevisionWithRetries();
|
|
354
371
|
}
|
|
355
372
|
if (!options.skipLanguageChecks &&
|
|
356
|
-
|
|
373
|
+
this.features?.language &&
|
|
357
374
|
!this.features.language_version_matches &&
|
|
358
375
|
this.atLeast('2.7.0')) {
|
|
359
376
|
_log.info('language version mismatch. silently updating...');
|
|
@@ -409,7 +426,6 @@ class Device extends utils_1.TypedEmitter {
|
|
|
409
426
|
return this.state[this.instance];
|
|
410
427
|
}
|
|
411
428
|
setState(state) {
|
|
412
|
-
var _a;
|
|
413
429
|
if (!state) {
|
|
414
430
|
delete this.state[this.instance];
|
|
415
431
|
}
|
|
@@ -420,11 +436,10 @@ class Device extends utils_1.TypedEmitter {
|
|
|
420
436
|
...state,
|
|
421
437
|
};
|
|
422
438
|
this.state[this.instance] = newState;
|
|
423
|
-
|
|
439
|
+
this.stateStorage?.saveState(this, newState);
|
|
424
440
|
}
|
|
425
441
|
}
|
|
426
442
|
async validateState(preauthorized = false) {
|
|
427
|
-
var _a;
|
|
428
443
|
if (!this.features)
|
|
429
444
|
return;
|
|
430
445
|
if (!this.features.unlocked && preauthorized) {
|
|
@@ -432,7 +447,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
432
447
|
return;
|
|
433
448
|
}
|
|
434
449
|
}
|
|
435
|
-
const expectedState =
|
|
450
|
+
const expectedState = this.getState()?.staticSessionId;
|
|
436
451
|
const state = await this.getCommands().getDeviceState();
|
|
437
452
|
const uniqueState = `${state}@${this.features.device_id}:${this.instance}`;
|
|
438
453
|
if (this.features.session_id) {
|
|
@@ -458,7 +473,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
458
473
|
}
|
|
459
474
|
const { message } = await this.getCommands().typedCall('Initialize', 'Features', payload);
|
|
460
475
|
this._updateFeatures(message);
|
|
461
|
-
this.setState({ deriveCardano: payload
|
|
476
|
+
this.setState({ deriveCardano: payload?.derive_cardano });
|
|
462
477
|
}
|
|
463
478
|
initStorage(storage) {
|
|
464
479
|
this.stateStorage = storage;
|
|
@@ -469,15 +484,14 @@ class Device extends utils_1.TypedEmitter {
|
|
|
469
484
|
this._updateFeatures(message);
|
|
470
485
|
}
|
|
471
486
|
async checkFirmwareHashWithRetries() {
|
|
472
|
-
var _a;
|
|
473
487
|
const lastResult = this.authenticityChecks.firmwareHash;
|
|
474
488
|
const notDoneYet = lastResult === null;
|
|
475
|
-
const attemptsDone =
|
|
489
|
+
const attemptsDone = lastResult?.attemptCount ?? 0;
|
|
476
490
|
if (attemptsDone >= constants_1.FIRMWARE.HASH_CHECK_MAX_ATTEMPTS)
|
|
477
491
|
return;
|
|
478
492
|
const wasError = lastResult !== null && !lastResult.success;
|
|
479
493
|
const wasErrorRetriable = wasError && (0, utils_1.isArrayMember)(lastResult.error, constants_1.FIRMWARE.HASH_CHECK_RETRIABLE_ERRORS);
|
|
480
|
-
const lastErrorPayload = wasError ? lastResult
|
|
494
|
+
const lastErrorPayload = wasError ? lastResult?.errorPayload : null;
|
|
481
495
|
if (notDoneYet || wasErrorRetriable) {
|
|
482
496
|
const result = await this.checkFirmwareHash();
|
|
483
497
|
this.authenticityChecks.firmwareHash = result;
|
|
@@ -490,7 +504,6 @@ class Device extends utils_1.TypedEmitter {
|
|
|
490
504
|
}
|
|
491
505
|
}
|
|
492
506
|
async checkFirmwareHash() {
|
|
493
|
-
var _a;
|
|
494
507
|
const createFailResult = (error, errorPayload) => ({
|
|
495
508
|
success: false,
|
|
496
509
|
error,
|
|
@@ -523,7 +536,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
523
536
|
const { hash: expectedHash, challenge } = (0, firmware_1.calculateFirmwareHash)(this.features.major_version, strippedBinary, (0, crypto_1.randomBytes)(32));
|
|
524
537
|
try {
|
|
525
538
|
const deviceResponse = await this.getCommands().typedCall('GetFirmwareHash', 'FirmwareHash', { challenge });
|
|
526
|
-
if (!
|
|
539
|
+
if (!deviceResponse?.message?.hash) {
|
|
527
540
|
return createFailResult('other-error', 'Device response is missing hash');
|
|
528
541
|
}
|
|
529
542
|
if (deviceResponse.message.hash !== expectedHash) {
|
|
@@ -560,7 +573,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
560
573
|
internalModel: this.features.internal_model,
|
|
561
574
|
deviceRevision: this.features.revision,
|
|
562
575
|
firmwareVersion,
|
|
563
|
-
expectedRevision: release
|
|
576
|
+
expectedRevision: release?.firmware_revision,
|
|
564
577
|
});
|
|
565
578
|
}
|
|
566
579
|
async changeLanguage({ language, binary, }) {
|
|
@@ -605,13 +618,12 @@ class Device extends utils_1.TypedEmitter {
|
|
|
605
618
|
return response.message;
|
|
606
619
|
}
|
|
607
620
|
_updateFeatures(feat) {
|
|
608
|
-
var _a, _b, _c, _d;
|
|
609
621
|
const capabilities = (0, deviceFeaturesUtils_1.parseCapabilities)(feat);
|
|
610
622
|
feat.capabilities = capabilities;
|
|
611
623
|
if (this.features && this.features.session_id && !feat.session_id) {
|
|
612
624
|
feat.session_id = this.features.session_id;
|
|
613
625
|
}
|
|
614
|
-
feat.unlocked =
|
|
626
|
+
feat.unlocked = feat.unlocked ?? true;
|
|
615
627
|
const revision = (0, deviceFeaturesUtils_1.parseRevision)(feat);
|
|
616
628
|
feat.revision = revision;
|
|
617
629
|
if (!feat.model && feat.major_version === 1) {
|
|
@@ -637,7 +649,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
637
649
|
this._unavailableCapabilities = (0, deviceFeaturesUtils_1.getUnavailableCapabilities)(feat, (0, coinInfo_1.getAllNetworks)());
|
|
638
650
|
this._firmwareStatus = (0, firmwareInfo_1.getFirmwareStatus)(feat);
|
|
639
651
|
this._firmwareRelease = (0, firmwareInfo_1.getRelease)(feat);
|
|
640
|
-
this.availableTranslations =
|
|
652
|
+
this.availableTranslations = this.firmwareRelease?.translations ?? [];
|
|
641
653
|
}
|
|
642
654
|
this._features = feat;
|
|
643
655
|
this._featuresNeedsReload = false;
|
|
@@ -655,12 +667,12 @@ class Device extends utils_1.TypedEmitter {
|
|
|
655
667
|
? types_1.FirmwareType.BitcoinOnly
|
|
656
668
|
: types_1.FirmwareType.Regular;
|
|
657
669
|
}
|
|
658
|
-
const deviceInfo =
|
|
670
|
+
const deviceInfo = models_1.models[feat.internal_model] ?? {
|
|
659
671
|
name: `Unknown ${feat.internal_model}`,
|
|
660
672
|
colors: {},
|
|
661
673
|
};
|
|
662
674
|
this.name = deviceInfo.name;
|
|
663
|
-
if (feat
|
|
675
|
+
if (feat?.unit_color) {
|
|
664
676
|
const deviceUnitColor = feat.unit_color.toString();
|
|
665
677
|
if (deviceUnitColor in deviceInfo.colors) {
|
|
666
678
|
this.color = deviceInfo.colors[deviceUnitColor];
|
|
@@ -674,9 +686,8 @@ class Device extends utils_1.TypedEmitter {
|
|
|
674
686
|
return !!this.unreadableError;
|
|
675
687
|
}
|
|
676
688
|
disconnect() {
|
|
677
|
-
var _a;
|
|
678
689
|
_log.debug('Disconnect cleanup');
|
|
679
|
-
|
|
690
|
+
this.sessionDfd?.reject(new Error());
|
|
680
691
|
this.lastAcquiredHere = false;
|
|
681
692
|
this.emitLifecycle(events_1.DEVICE.DISCONNECT);
|
|
682
693
|
return this.interruptionFromUser(constants_1.ERRORS.TypedError('Device_Disconnected'));
|
|
@@ -754,12 +765,11 @@ class Device extends utils_1.TypedEmitter {
|
|
|
754
765
|
return null;
|
|
755
766
|
}
|
|
756
767
|
async dispose() {
|
|
757
|
-
var _a, _b;
|
|
758
768
|
this.removeAllListeners();
|
|
759
769
|
if (this.session && this.lastAcquiredHere) {
|
|
760
770
|
try {
|
|
761
|
-
await
|
|
762
|
-
await
|
|
771
|
+
await this.cancelableAction?.();
|
|
772
|
+
await this.commands?.cancel();
|
|
763
773
|
return this.transport.release({
|
|
764
774
|
session: this.session,
|
|
765
775
|
path: this.transportPath,
|
|
@@ -780,7 +790,6 @@ class Device extends utils_1.TypedEmitter {
|
|
|
780
790
|
return 'normal';
|
|
781
791
|
}
|
|
782
792
|
toMessageObject() {
|
|
783
|
-
var _a;
|
|
784
793
|
const { name, uniquePath: path } = this;
|
|
785
794
|
const base = { path, name };
|
|
786
795
|
if (this.unreadableError) {
|
|
@@ -812,7 +821,7 @@ class Device extends utils_1.TypedEmitter {
|
|
|
812
821
|
id: this.features.device_id,
|
|
813
822
|
label,
|
|
814
823
|
_state: this.getState(),
|
|
815
|
-
state:
|
|
824
|
+
state: this.getState()?.staticSessionId,
|
|
816
825
|
status,
|
|
817
826
|
mode: this.getMode(),
|
|
818
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({
|
package/lib/device/prompts.js
CHANGED
|
@@ -22,13 +22,10 @@ const cancelPrompt = (device, expectResponse = true) => {
|
|
|
22
22
|
};
|
|
23
23
|
exports.cancelPrompt = cancelPrompt;
|
|
24
24
|
const prompt = (event, ...[device, ...args]) => new Promise((resolve, reject) => {
|
|
25
|
-
const cancelAndReject = (error) => (0, exports.cancelPrompt)(device).then(onCancel =>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
? (_a = onCancel.payload) === null || _a === void 0 ? void 0 : _a.message.message
|
|
30
|
-
: onCancel.error));
|
|
31
|
-
});
|
|
25
|
+
const cancelAndReject = (error) => (0, exports.cancelPrompt)(device).then(onCancel => reject(error ||
|
|
26
|
+
new Error(onCancel.success
|
|
27
|
+
? onCancel.payload?.message.message
|
|
28
|
+
: onCancel.error)));
|
|
32
29
|
if (device.listenerCount(event) > 0) {
|
|
33
30
|
device.setCancelableAction(cancelAndReject);
|
|
34
31
|
const callback = (...[response, error]) => {
|