@trezor/connect
Version:
High-level javascript interface for Trezor hardware wallet.
844 lines • 32.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Device = exports.GET_FEATURES_TIMEOUT_REACT_NATIVE = exports.GET_FEATURES_TIMEOUT = exports.CANCEL_TIMEOUT = void 0;
const crypto_1 = require("crypto");
const protocol_1 = require("@trezor/protocol");
const transport_1 = require("@trezor/transport");
const utils_1 = require("@trezor/utils");
const DeviceCommands_1 = require("./DeviceCommands");
const constants_1 = require("../constants");
const checkFirmwareRevision_1 = require("./checkFirmwareRevision");
const prompts_1 = require("./prompts");
const firmware_1 = require("../api/firmware");
const DataManager_1 = require("../data/DataManager");
const coinInfo_1 = require("../data/coinInfo");
const firmwareInfo_1 = require("../data/firmwareInfo");
const getLanguage_1 = require("../data/getLanguage");
const models_1 = require("../data/models");
const events_1 = require("../events");
const types_1 = require("../types");
const debug_1 = require("../utils/debug");
const deviceFeaturesUtils_1 = require("../utils/deviceFeaturesUtils");
const _log = (0, debug_1.initLog)('Device');
exports.CANCEL_TIMEOUT = 1_000;
exports.GET_FEATURES_TIMEOUT = 3_000;
exports.GET_FEATURES_TIMEOUT_REACT_NATIVE = 20_000;
const parseRunOptions = (options) => {
if (!options)
options = {};
return options;
};
class Device extends utils_1.TypedEmitter {
transport;
protocol;
transportPath;
transportSessionOwner;
transportDescriptorType;
bluetoothProps;
session;
lastAcquiredHere;
unreadableError;
_firmwareStatus;
get firmwareStatus() {
return this._firmwareStatus;
}
_firmwareRelease;
get firmwareRelease() {
return this._firmwareRelease;
}
_features;
get features() {
return this._features;
}
_featuresNeedsReload = false;
acquirePromise;
releasePromise;
runPromise;
keepTransportSession = false;
commands;
cancelableAction;
loaded = false;
inconsistent = false;
firstRunPromise;
instance = 0;
state = [];
stateStorage = undefined;
_unavailableCapabilities = {};
get unavailableCapabilities() {
return this._unavailableCapabilities;
}
_firmwareType;
get firmwareType() {
return this._firmwareType;
}
name = 'Trezor';
color;
availableTranslations = [];
authenticityChecks = {
firmwareRevision: null,
firmwareHash: null,
};
uniquePath;
emitLifecycle;
sessionDfd;
handshakeFinished = false;
constructor({ id, transport, descriptor, listener }) {
super();
this.emitLifecycle = listener;
this.protocol = protocol_1.v1;
this.uniquePath = id;
this.transport = transport;
this.transportPath = descriptor.path;
this.transportSessionOwner = descriptor.sessionOwner;
this.transportDescriptorType = descriptor.type;
this.bluetoothProps = descriptor.id ? { id: descriptor.id } : undefined;
this.session = descriptor.session;
this.lastAcquiredHere = false;
this.firstRunPromise = (0, utils_1.createDeferred)();
}
getSessionChangePromise() {
if (!this.sessionDfd) {
this.sessionDfd = (0, utils_1.createDeferred)();
this.sessionDfd.promise
.catch(() => { })
.finally(() => {
this.sessionDfd = undefined;
});
}
return this.sessionDfd.promise;
}
async waitAndCompareSession(response, sessionPromise) {
if (response.success) {
try {
if ((await sessionPromise) !== response.payload) {
return {
success: false,
error: transport_1.TRANSPORT_ERROR.SESSION_WRONG_PREVIOUS,
};
}
}
catch {
return {
success: false,
error: transport_1.TRANSPORT_ERROR.DEVICE_DISCONNECTED_DURING_ACTION,
};
}
}
return response;
}
acquire() {
const sessionPromise = this.getSessionChangePromise();
this.acquirePromise = this.transport
.acquire({ input: { path: this.transportPath, previous: this.session } })
.then(result => this.waitAndCompareSession(result, sessionPromise))
.then(result => {
if (result.success) {
this.session = result.payload;
this.lastAcquiredHere = true;
this.commands?.dispose();
this.commands = new DeviceCommands_1.DeviceCommands(this, this.transport, this.session);
return result;
}
else {
if (this.runPromise) {
this.runPromise.reject(new Error(result.error));
delete this.runPromise;
}
throw result.error;
}
})
.finally(() => {
this.acquirePromise = undefined;
});
return this.acquirePromise;
}
async release() {
const localSession = this.getLocalSession();
if (!localSession || this.keepTransportSession || this.releasePromise) {
return;
}
if (this.commands) {
this.commands.dispose();
if (this.commands.callPromise) {
await this.commands.callPromise;
}
}
const sessionPromise = this.getSessionChangePromise();
this.releasePromise = this.transport
.release({ session: localSession, path: this.transportPath })
.then(result => this.waitAndCompareSession(result, sessionPromise))
.then(result => {
if (result.success) {
this.session = null;
}
return result;
})
.finally(() => {
this.releasePromise = undefined;
});
return this.releasePromise;
}
releaseTransportSession() {
this.keepTransportSession = false;
}
async cleanup(release = true) {
this.eventNames().forEach(e => this.removeAllListeners(e));
delete this.runPromise;
if (release) {
await this.release();
}
}
async handshake(delay) {
if (delay) {
await (0, utils_1.resolveAfter)(501 + delay);
}
while (true) {
if (this.isUsedElsewhere()) {
this.emitLifecycle(events_1.DEVICE.CONNECT_UNACQUIRED);
}
else {
try {
await this.run();
}
catch (error) {
_log.warn(`device.run error.message: ${error.message}, code: ${error.code}`);
if (error.code === 'Device_NotFound' ||
error.message === transport_1.TRANSPORT_ERROR.DEVICE_NOT_FOUND ||
error.message === transport_1.TRANSPORT_ERROR.DEVICE_DISCONNECTED_DURING_ACTION ||
error.message === transport_1.TRANSPORT_ERROR.DESCRIPTOR_NOT_FOUND ||
error.message === transport_1.TRANSPORT_ERROR.HTTP_ERROR) {
}
else if (error.message === transport_1.TRANSPORT_ERROR.UNEXPECTED_ERROR ||
error.message === transport_1.TRANSPORT_ERROR.SESSION_WRONG_PREVIOUS ||
error.code === 'Device_UsedElsewhere' ||
error.code === 'Device_InitializeFailed') {
this.emitLifecycle(events_1.DEVICE.CONNECT_UNACQUIRED);
}
else if (error.message === transport_1.TRANSPORT_ERROR.INTERFACE_UNABLE_TO_OPEN_DEVICE ||
error.message?.indexOf(constants_1.ERRORS.LIBUSB_ERROR_MESSAGE) >= 0) {
this.unreadableError = error?.message;
this.emitLifecycle(events_1.DEVICE.CONNECT_UNACQUIRED);
}
else {
await (0, utils_1.resolveAfter)(501);
continue;
}
}
}
this.handshakeFinished = true;
return;
}
}
async updateDescriptor(descriptor) {
this.sessionDfd?.resolve(descriptor.session);
await Promise.all([this.acquirePromise, this.releasePromise]);
if (descriptor.session && descriptor.session !== this.session) {
this.usedElsewhere();
}
if (!descriptor.session) {
const methodStillRunning = !this.commands?.isDisposed();
if (methodStillRunning) {
this.releaseTransportSession();
}
}
this.session = descriptor.session;
this.emitLifecycle(events_1.DEVICE.CHANGED);
}
run(fn, options) {
if (this.runPromise) {
_log.warn('Previous call is still running');
throw constants_1.ERRORS.TypedError('Device_CallInProgress');
}
options = parseRunOptions(options);
const wasUnacquired = this.isUnacquired();
const runPromise = (0, utils_1.createDeferred)();
this.runPromise = runPromise;
this._runInner(fn, options)
.then(() => {
if (wasUnacquired && !this.isUnacquired()) {
this.emitLifecycle(events_1.DEVICE.CONNECT);
}
})
.catch(err => {
runPromise.reject(err);
});
return runPromise.promise;
}
async override(error) {
if (this.acquirePromise) {
await this.acquirePromise;
}
if (this.runPromise) {
await this.interruptionFromUser(error);
}
if (this.releasePromise) {
await this.releasePromise;
}
}
setCancelableAction(callback) {
this.cancelableAction = (e) => callback(e)
.catch(e2 => {
_log.debug('cancelableAction error', e2);
})
.finally(() => {
this.clearCancelableAction();
});
}
clearCancelableAction() {
this.cancelableAction = undefined;
}
async interruptionFromUser(error) {
_log.debug('interruptionFromUser');
await this.cancelableAction?.(error);
await this.commands?.cancel();
if (this.runPromise) {
this.runPromise.reject(error);
delete this.runPromise;
}
}
usedElsewhere() {
if (!this.lastAcquiredHere) {
return;
}
this.lastAcquiredHere = false;
this._featuresNeedsReload = true;
_log.debug('interruptionFromOutside');
if (this.commands) {
this.commands.dispose();
}
if (this.runPromise) {
this.runPromise.reject(constants_1.ERRORS.TypedError('Device_UsedElsewhere'));
delete this.runPromise;
}
if (this.session) {
this.transport.releaseDevice(this.session);
}
}
async _runInner(fn, options) {
if (this.releasePromise) {
await this.releasePromise;
}
const acquireNeeded = !this.isUsedHere() || this.commands?.disposed;
if (acquireNeeded) {
await this.acquire();
}
const { staticSessionId, deriveCardano } = this.getState() || {};
if (acquireNeeded || !staticSessionId || (!deriveCardano && options.useCardanoDerivation)) {
try {
if (fn) {
await this.initialize(!!options.useCardanoDerivation);
}
else {
const isNative = DataManager_1.DataManager.getSettings('env') === 'react-native';
const getFeaturesTimeout = isNative
? exports.GET_FEATURES_TIMEOUT_REACT_NATIVE
: exports.GET_FEATURES_TIMEOUT;
const cancelTimeout = isNative
? exports.GET_FEATURES_TIMEOUT_REACT_NATIVE
: exports.CANCEL_TIMEOUT;
if (['v1', 'bridge'].includes(this.protocol.name) &&
![0, 2].includes(this.transportDescriptorType)) {
_log.debug('sending a preventive cancel on the first encounter with the device');
await Promise.race([
this.getCommands()
.typedCall('Cancel', 'Failure', {})
.catch(() => { }),
new Promise((_, reject) => setTimeout(reject, cancelTimeout)),
]).catch(() => this.acquire());
}
let getFeaturesTimeoutId;
await Promise.race([
this.getFeatures().finally(() => clearTimeout(getFeaturesTimeoutId)),
new Promise((_resolve, reject) => {
getFeaturesTimeoutId = setTimeout(() => {
(0, prompts_1.cancelPrompt)(this, false).finally(() => {
reject(new Error('GetFeatures timeout'));
});
}, getFeaturesTimeout);
}),
]);
}
}
catch (error) {
_log.warn('Device._runInner error: ', error.message);
if (!this.inconsistent &&
(error.message === 'GetFeatures timeout' || error.message === 'Unknown message')) {
this.inconsistent = true;
return this._runInner(() => Promise.resolve({}), options);
}
if (transport_1.TRANSPORT_ERROR.ABORTED_BY_TIMEOUT === error.message) {
this.unreadableError = 'Connection timeout';
}
this.inconsistent = true;
delete this.runPromise;
return Promise.reject(constants_1.ERRORS.TypedError('Device_InitializeFailed', `Initialize failed: ${error.message}${error.code ? `, code: ${error.code}` : ''}`));
}
}
if (!options.skipFirmwareChecks) {
await this.checkFirmwareHashWithRetries();
await this.checkFirmwareRevisionWithRetries();
}
if (!options.skipLanguageChecks &&
this.features?.language &&
!this.features.language_version_matches &&
this.atLeast('2.7.0')) {
_log.info('language version mismatch. silently updating...');
try {
await this.changeLanguage({ language: this.features.language });
}
catch (err) {
_log.error('change language failed silently', err);
}
}
if (options.keepSession) {
this.keepTransportSession = true;
}
if (fn) {
await fn();
}
if (this.loaded && this.features && !options.skipFinalReload) {
await this.getFeatures();
}
if ((!this.keepTransportSession && typeof options.keepSession !== 'boolean') ||
options.keepSession === false) {
this.keepTransportSession = false;
await this.release();
}
if (this.runPromise) {
this.runPromise.resolve();
}
delete this.runPromise;
if (!this.loaded) {
this.loaded = true;
this.firstRunPromise.resolve(true);
}
}
getCommands() {
if (!this.commands) {
throw constants_1.ERRORS.TypedError('Runtime', `Device: commands not defined`);
}
return this.commands;
}
setInstance(instance = 0) {
if (this.instance !== instance) {
if (this.keepTransportSession) {
this.lastAcquiredHere = false;
this.keepTransportSession = false;
}
}
this.instance = instance;
}
getInstance() {
return this.instance;
}
getState() {
return this.state[this.instance];
}
setState(state) {
if (!state) {
delete this.state[this.instance];
}
else {
const prevState = this.state[this.instance];
const newState = {
...prevState,
...state,
};
this.state[this.instance] = newState;
this.stateStorage?.saveState(this, newState);
}
}
async validateState(preauthorized = false) {
if (!this.features)
return;
if (!this.features.unlocked && preauthorized) {
if (await this.getCommands().preauthorize(false)) {
return;
}
}
const expectedState = this.getState()?.staticSessionId;
const state = await this.getCommands().getDeviceState();
const uniqueState = `${state}@${this.features.device_id}:${this.instance}`;
if (this.features.session_id) {
this.setState({ sessionId: this.features.session_id });
}
if (expectedState && expectedState !== uniqueState) {
return uniqueState;
}
if (!expectedState) {
this.setState({ staticSessionId: uniqueState });
}
}
async initialize(useCardanoDerivation) {
let payload;
if (this.features) {
const { sessionId, deriveCardano } = this.getState() || {};
payload = {
derive_cardano: deriveCardano || useCardanoDerivation,
};
if (sessionId) {
payload.session_id = sessionId;
}
}
const { message } = await this.getCommands().typedCall('Initialize', 'Features', payload);
this._updateFeatures(message);
this.setState({ deriveCardano: payload?.derive_cardano });
}
initStorage(storage) {
this.stateStorage = storage;
this.setState(storage.loadState(this));
}
async getFeatures() {
const { message } = await this.getCommands().typedCall('GetFeatures', 'Features', {});
this._updateFeatures(message);
}
async checkFirmwareHashWithRetries() {
const lastResult = this.authenticityChecks.firmwareHash;
const notDoneYet = lastResult === null;
const attemptsDone = lastResult?.attemptCount ?? 0;
if (attemptsDone >= constants_1.FIRMWARE.HASH_CHECK_MAX_ATTEMPTS)
return;
const wasError = lastResult !== null && !lastResult.success;
const wasErrorRetriable = wasError && (0, utils_1.isArrayMember)(lastResult.error, constants_1.FIRMWARE.HASH_CHECK_RETRIABLE_ERRORS);
const lastErrorPayload = wasError ? lastResult?.errorPayload : null;
if (notDoneYet || wasErrorRetriable) {
const result = await this.checkFirmwareHash();
this.authenticityChecks.firmwareHash = result;
if (result === null)
return;
result.attemptCount = attemptsDone + 1;
if (result.success && lastErrorPayload) {
result.warningPayload = { lastErrorPayload, successOnAttempt: result.attemptCount };
}
}
}
async checkFirmwareHash() {
const createFailResult = (error, errorPayload) => ({
success: false,
error,
errorPayload,
});
const baseUrl = DataManager_1.DataManager.getSettings('binFilesBaseUrl');
const enabled = DataManager_1.DataManager.getSettings('enableFirmwareHashCheck');
if (!enabled || baseUrl === undefined)
return createFailResult('check-skipped');
const firmwareVersion = this.getVersion();
if (firmwareVersion === undefined || !this.features || this.features.bootloader_mode) {
return null;
}
const checkSupported = this.atLeast(constants_1.FIRMWARE.FW_HASH_SUPPORTED_VERSIONS);
if (!checkSupported)
return createFailResult('check-unsupported');
const release = (0, firmwareInfo_1.getReleases)(this.features.internal_model).find(r => utils_1.versionUtils.isEqual(r.version, firmwareVersion));
if (release === undefined)
return createFailResult('unknown-release');
const btcOnly = this.firmwareType === types_1.FirmwareType.BitcoinOnly;
const binary = await (0, firmware_1.getBinaryOptional)({ baseUrl, btcOnly, release });
if (binary === null) {
return createFailResult('check-unsupported');
}
if (binary.byteLength < 200) {
_log.warn(`Firmware binary for hash check suspiciously small (< 200 b)`);
return createFailResult('check-unsupported');
}
const strippedBinary = (0, firmware_1.stripFwHeaders)(binary);
const { hash: expectedHash, challenge } = (0, firmware_1.calculateFirmwareHash)(this.features.major_version, strippedBinary, (0, crypto_1.randomBytes)(32));
try {
const deviceResponse = await this.getCommands().typedCall('GetFirmwareHash', 'FirmwareHash', { challenge });
if (!deviceResponse?.message?.hash) {
return createFailResult('other-error', 'Device response is missing hash');
}
if (deviceResponse.message.hash !== expectedHash) {
return createFailResult('hash-mismatch');
}
return { success: true };
}
catch (errorPayload) {
return createFailResult('other-error', (0, utils_1.serializeError)(errorPayload));
}
}
async checkFirmwareRevisionWithRetries() {
const lastResult = this.authenticityChecks.firmwareRevision;
const notDoneYet = lastResult === null;
const wasError = lastResult !== null && !lastResult.success;
const wasErrorRetriable = wasError && (0, utils_1.isArrayMember)(lastResult.error, constants_1.FIRMWARE.REVISION_CHECK_RETRIABLE_ERRORS);
if (notDoneYet || wasErrorRetriable) {
await this.checkFirmwareRevision();
}
}
async checkFirmwareRevision() {
const firmwareVersion = this.getVersion();
if (!firmwareVersion || !this.features) {
return;
}
if (this.features.bootloader_mode === true) {
return;
}
const releases = (0, firmwareInfo_1.getReleases)(this.features.internal_model);
const release = releases.find(r => firmwareVersion &&
utils_1.versionUtils.isVersionArray(firmwareVersion) &&
utils_1.versionUtils.isEqual(r.version, firmwareVersion));
this.authenticityChecks.firmwareRevision = await (0, checkFirmwareRevision_1.checkFirmwareRevision)({
internalModel: this.features.internal_model,
deviceRevision: this.features.revision,
firmwareVersion,
expectedRevision: release?.firmware_revision,
});
}
async changeLanguage({ language, binary, }) {
if (language === 'en-US') {
return this._uploadTranslationData(null);
}
if (binary) {
return this._uploadTranslationData(binary);
}
const version = this.getVersion();
if (!version) {
throw constants_1.ERRORS.TypedError('Runtime', 'changeLanguage: device version unknown');
}
const downloadedBinary = await (0, getLanguage_1.getLanguage)({
language,
version,
internal_model: this.features.internal_model,
});
if (!downloadedBinary) {
throw constants_1.ERRORS.TypedError('Runtime', 'changeLanguage: translation not found');
}
return this._uploadTranslationData(downloadedBinary);
}
async _uploadTranslationData(payload) {
if (!this.commands) {
throw constants_1.ERRORS.TypedError('Runtime', 'uploadTranslationData: device.commands is not set');
}
if (payload === null) {
const response = await this.commands.typedCall('ChangeLanguage', ['Success'], { data_length: 0 });
return response.message;
}
const length = payload.byteLength;
let response = await this.commands.typedCall('ChangeLanguage', ['TranslationDataRequest', 'Success'], { data_length: length });
while (response.type !== 'Success') {
const start = response.message.data_offset;
const end = response.message.data_offset + response.message.data_length;
const chunk = payload.slice(start, end);
response = await this.commands.typedCall('TranslationDataAck', ['TranslationDataRequest', 'Success'], {
data_chunk: Buffer.from(chunk).toString('hex'),
});
}
return response.message;
}
_updateFeatures(feat) {
const capabilities = (0, deviceFeaturesUtils_1.parseCapabilities)(feat);
feat.capabilities = capabilities;
if (this.features && this.features.session_id && !feat.session_id) {
feat.session_id = this.features.session_id;
}
feat.unlocked = feat.unlocked ?? true;
const revision = (0, deviceFeaturesUtils_1.parseRevision)(feat);
feat.revision = revision;
if (!feat.model && feat.major_version === 1) {
feat.model = '1';
}
if (!feat.internal_model || !types_1.DeviceModelInternal[feat.internal_model]) {
feat.internal_model = (0, deviceFeaturesUtils_1.ensureInternalModelFeature)(feat.model);
}
const version = this.getVersion();
const newVersion = [
feat.major_version,
feat.minor_version,
feat.patch_version,
];
if (!version || !utils_1.versionUtils.isEqual(version, newVersion)) {
if (version) {
this.emit(events_1.DEVICE.FIRMWARE_VERSION_CHANGED, {
oldVersion: version,
newVersion,
device: this.toMessageObject(),
});
}
this._unavailableCapabilities = (0, deviceFeaturesUtils_1.getUnavailableCapabilities)(feat, (0, coinInfo_1.getAllNetworks)());
this._firmwareStatus = (0, firmwareInfo_1.getFirmwareStatus)(feat);
this._firmwareRelease = (0, firmwareInfo_1.getRelease)(feat);
this.availableTranslations = this.firmwareRelease?.translations ?? [];
}
this._features = feat;
this._featuresNeedsReload = false;
if (feat.fw_vendor === 'Trezor Bitcoin-only') {
this._firmwareType = types_1.FirmwareType.BitcoinOnly;
}
else if (feat.fw_vendor === 'Trezor') {
this._firmwareType = types_1.FirmwareType.Regular;
}
else if (this.getMode() !== 'bootloader') {
this._firmwareType =
feat.capabilities &&
feat.capabilities.length > 0 &&
!feat.capabilities.includes('Capability_Bitcoin_like')
? types_1.FirmwareType.BitcoinOnly
: types_1.FirmwareType.Regular;
}
const deviceInfo = models_1.models[feat.internal_model] ?? {
name: `Unknown ${feat.internal_model}`,
colors: {},
};
this.name = deviceInfo.name;
if (feat?.unit_color) {
const deviceUnitColor = feat.unit_color.toString();
if (deviceUnitColor in deviceInfo.colors) {
this.color = deviceInfo.colors[deviceUnitColor];
}
}
}
isUnacquired() {
return this.features === undefined;
}
isUnreadable() {
return !!this.unreadableError;
}
disconnect() {
_log.debug('Disconnect cleanup');
this.sessionDfd?.reject(new Error());
this.lastAcquiredHere = false;
this.emitLifecycle(events_1.DEVICE.DISCONNECT);
return this.interruptionFromUser(constants_1.ERRORS.TypedError('Device_Disconnected'));
}
isBootloader() {
return this.features && !!this.features.bootloader_mode;
}
isInitialized() {
return this.features && !!this.features.initialized;
}
isSeedless() {
return this.features && !!this.features.no_backup;
}
isInconsistent() {
return this.inconsistent;
}
getVersion() {
if (!this.features)
return;
return [
this.features.major_version,
this.features.minor_version,
this.features.patch_version,
];
}
atLeast(versions) {
const version = this.getVersion();
if (!this.features || !version)
return false;
const modelVersion = typeof versions === 'string' ? versions : versions[this.features.major_version - 1];
return utils_1.versionUtils.isNewerOrEqual(version, modelVersion);
}
isUsed() {
return typeof this.session === 'string';
}
isUsedHere() {
return this.isUsed() && this.lastAcquiredHere;
}
isUsedElsewhere() {
return this.isUsed() && !this.lastAcquiredHere;
}
isRunning() {
return !!this.runPromise;
}
isLoaded() {
return this.loaded;
}
waitForFirstRun() {
return this.firstRunPromise.promise;
}
getLocalSession() {
return this.lastAcquiredHere ? this.session : null;
}
getUniquePath() {
return this.uniquePath;
}
isT1() {
return this.features ? this.features.major_version === 1 : false;
}
hasUnexpectedMode(allow, require) {
if (this.features) {
if (this.isBootloader() && !allow.includes(events_1.UI.BOOTLOADER)) {
return events_1.UI.BOOTLOADER;
}
if (!this.isInitialized() && !allow.includes(events_1.UI.INITIALIZE)) {
return events_1.UI.INITIALIZE;
}
if (this.isSeedless() && !allow.includes(events_1.UI.SEEDLESS)) {
return events_1.UI.SEEDLESS;
}
if (!this.isBootloader() && require.includes(events_1.UI.BOOTLOADER)) {
return events_1.UI.NOT_IN_BOOTLOADER;
}
}
return null;
}
dispose() {
this.removeAllListeners();
if (this.session && this.lastAcquiredHere) {
return this.transport.release({
session: this.session,
path: this.transportPath,
onClose: true,
});
}
}
getMode() {
if (this.features.bootloader_mode)
return 'bootloader';
if (!this.features.initialized)
return 'initialize';
if (this.features.no_backup)
return 'seedless';
return 'normal';
}
toMessageObject() {
const { name, uniquePath: path } = this;
const base = { path, name };
if (this.unreadableError) {
return {
...base,
type: 'unreadable',
error: this.unreadableError,
label: 'Unreadable device',
transportDescriptorType: this.transportDescriptorType,
};
}
if (this.isUnacquired()) {
return {
...base,
type: 'unacquired',
label: 'Unacquired device',
name: this.name,
transportSessionOwner: this.transportSessionOwner,
bluetoothProps: this.bluetoothProps,
};
}
const defaultLabel = 'My Trezor';
const label = this.features.label === '' || !this.features.label ? defaultLabel : this.features.label;
let status = this.isUsedElsewhere() ? 'occupied' : 'available';
if (this._featuresNeedsReload)
status = 'used';
return {
...base,
type: 'acquired',
id: this.features.device_id,
label,
_state: this.getState(),
state: this.getState()?.staticSessionId,
status,
mode: this.getMode(),
color: this.color,
firmware: this.firmwareStatus,
firmwareRelease: this.firmwareRelease,
firmwareType: this.firmwareType,
features: this.features,
unavailableCapabilities: this.unavailableCapabilities,
availableTranslations: this.availableTranslations,
authenticityChecks: this.authenticityChecks,
bluetoothProps: this.bluetoothProps,
};
}
}
exports.Device = Device;
//# sourceMappingURL=Device.js.map