diff --git a/packages/connect/src/device/Device.ts b/packages/connect/src/device/Device.ts index 6fb5ed95755..19a338d616d 100644 --- a/packages/connect/src/device/Device.ts +++ b/packages/connect/src/device/Device.ts @@ -570,10 +570,12 @@ export class Device extends TypedEmitter { if ( // The check was not yet performed this.authenticityChecks.firmwareRevision === null || - // The check was performed, but outcome cannot be surely determined (Suite is offline) + // The check was performed, but outcome cannot be surely determined (Suite is offline or there was an unexpected error) // -> recheck on every getFeatures() until the result is known (this.authenticityChecks.firmwareRevision.success === false && - this.authenticityChecks.firmwareRevision.error === 'cannot-perform-check-offline') + ['cannot-perform-check-offline', 'other-error'].includes( + this.authenticityChecks.firmwareRevision.error, + )) ) { await this.checkFirmwareRevision(); } diff --git a/packages/connect/src/device/__tests__/checkFirmwareRevision.test.ts b/packages/connect/src/device/__tests__/checkFirmwareRevision.test.ts index 4e0d809e62b..d5ad53d5c91 100644 --- a/packages/connect/src/device/__tests__/checkFirmwareRevision.test.ts +++ b/packages/connect/src/device/__tests__/checkFirmwareRevision.test.ts @@ -1,3 +1,5 @@ +import { FetchError } from 'node-fetch'; + import { DeviceModelInternal } from '@trezor/protobuf'; import { checkFirmwareRevision, CheckFirmwareRevisionParams } from '../checkFirmwareRevision'; import { FirmwareRelease, FirmwareRevisionCheckResult } from '../../exports'; @@ -85,17 +87,29 @@ describe.each(DeviceNames)(`${checkFirmwareRevision.name} for device %s`, intern expected: { success: false, error: 'firmware-version-unknown' }, }, { - it: - 'returns null (NOT SUCCESS / NOT ERROR) when firmware version is not found locally, and the user is offline' + - 'In this case. User SHALL BE warned to go ONLINE and UPGRADE before doing anything!', + it: 'fails with a specific error message when the check cannot be performed because the revision is not found locally and the user is offline', httpRequestMock: () => { - throw new Error('You are offline!'); + throw new FetchError('You are offline!', 'network', { + code: 'ENOTFOUND', + name: 'FetchError', + message: 'You are offline!', + }); }, params: createDeviceParams({ expectedRevision: undefined, // firmware not known by local releases.json file }), expected: { success: false, error: 'cannot-perform-check-offline' }, }, + { + it: 'fails with a generic error message when there is an error when reading the online version of releases.json', + httpRequestMock: () => { + throw new Error('There is an unexpected error!'); + }, + params: createDeviceParams({ + expectedRevision: undefined, // firmware not known by local releases.json file + }), + expected: { success: false, error: 'other-error' }, + }, ])(`$it`, async ({ params, expected, httpRequestMock }) => { if (httpRequestMock !== undefined) { jest.spyOn(utilsAssets, 'httpRequest').mockImplementation(httpRequestMock); diff --git a/packages/connect/src/device/checkFirmwareRevision.ts b/packages/connect/src/device/checkFirmwareRevision.ts index 0ba9b16100c..ade88aa48f1 100644 --- a/packages/connect/src/device/checkFirmwareRevision.ts +++ b/packages/connect/src/device/checkFirmwareRevision.ts @@ -2,7 +2,7 @@ import { isEqual } from '@trezor/utils/src/versionUtils'; import { PROTO } from '../constants'; import { downloadReleasesMetadata } from '../data/downloadReleasesMetadata'; import { FirmwareRelease, VersionArray } from '../types'; -import { FirmwareRevisionCheckResult } from '../types/device'; +import { FirmwareRevisionCheckError, FirmwareRevisionCheckResult } from '../types/device'; import { calculateRevisionForDevice } from './calculateRevisionForDevice'; type GetOnlineReleaseMetadataParams = { @@ -20,8 +20,8 @@ const getOnlineReleaseMetadata = async ({ }; const failFirmwareRevisionCheck = ( - error: 'revision-mismatch' | 'firmware-version-unknown' | 'firmware-version-unknown', -): FirmwareRevisionCheckResult => ({ success: false, error }); + error: FirmwareRevisionCheckError, +): Extract => ({ success: false, error }); export type CheckFirmwareRevisionParams = { firmwareVersion: VersionArray; @@ -86,10 +86,11 @@ export const checkFirmwareRevision = async ({ return { success: true }; } catch (e) { - return { - success: false, - error: 'cannot-perform-check-offline', - }; + if (e.name === 'FetchError' && e.code === 'ENOTFOUND') { + return failFirmwareRevisionCheck('cannot-perform-check-offline'); + } + + return failFirmwareRevisionCheck('other-error'); } } diff --git a/packages/connect/src/types/device.ts b/packages/connect/src/types/device.ts index 288a1e54135..d1d4737d7e1 100644 --- a/packages/connect/src/types/device.ts +++ b/packages/connect/src/types/device.ts @@ -42,15 +42,17 @@ export type DeviceState = { // key = coin shortcut lowercase (ex: btc, eth, xrp) OR field declared in coins.json "supportedFirmware.capability" export type UnavailableCapabilities = { [key: string]: UnavailableCapability }; +export type FirmwareRevisionCheckError = + | 'revision-mismatch' + | 'firmware-version-unknown' + | 'cannot-perform-check-offline' // suite offline & release version not found locally => we cannot check with `data.trezor.io` + | 'other-error'; // incorrect URL, cannot parse JSON, etc. + export type FirmwareRevisionCheckResult = | { success: true } | { success: false; - error: - | 'revision-mismatch' - | 'firmware-version-unknown' - | 'firmware-version-unknown' - | 'cannot-perform-check-offline'; // suite offline & release version not found locally => we cannot check with `data.trezor.io` + error: FirmwareRevisionCheckError; }; export type KnownDevice = { diff --git a/packages/suite/src/components/suite/banners/SuiteBanners/FirmwareRevisionCheckBanner.tsx b/packages/suite/src/components/suite/banners/SuiteBanners/FirmwareRevisionCheckBanner.tsx index ef6902358c6..35cc6efd521 100644 --- a/packages/suite/src/components/suite/banners/SuiteBanners/FirmwareRevisionCheckBanner.tsx +++ b/packages/suite/src/components/suite/banners/SuiteBanners/FirmwareRevisionCheckBanner.tsx @@ -1,20 +1,32 @@ +import { TranslationKey } from '@suite-common/intl-types'; +import { isDeviceAcquired } from '@suite-common/suite-utils'; import { selectDevice } from '@suite-common/wallet-core'; import { Banner } from '@trezor/components'; +import { FirmwareRevisionCheckError } from '@trezor/connect'; import { HELP_CENTER_FIRMWARE_REVISION_CHECK } from '@trezor/urls'; import { Translation, TrezorLink } from 'src/components/suite'; import { useSelector } from 'src/hooks/suite'; +const messages: Record = { + 'cannot-perform-check-offline': 'TR_DEVICE_FIRMWARE_REVISION_CHECK_UNABLE_TO_PERFORM', + 'other-error': 'TR_FIRMWARE_REVISION_CHECK_OTHER_ERROR', + 'revision-mismatch': 'TR_FIRMWARE_REVISION_CHECK_FAILED', + 'firmware-version-unknown': 'TR_FIRMWARE_REVISION_CHECK_FAILED', +}; + export const FirmwareRevisionCheckBanner = () => { const device = useSelector(selectDevice); + if ( + !isDeviceAcquired(device) || + device.authenticityChecks?.firmwareRevision?.success !== false + ) { + return null; + } + const wasOffline = - device?.features && - device.authenticityChecks?.firmwareRevision?.success === false && device.authenticityChecks.firmwareRevision.error === 'cannot-perform-check-offline'; - const message = wasOffline - ? 'TR_DEVICE_FIRMWARE_REVISION_CHECK_UNABLE_TO_PERFORM' - : 'TR_FIRMWARE_REVISION_CHECK_FAILED'; return ( { ) } > - + ); }; diff --git a/packages/suite/src/reducers/suite/suiteReducer.ts b/packages/suite/src/reducers/suite/suiteReducer.ts index af3bf9efc0f..e4236ac6de7 100644 --- a/packages/suite/src/reducers/suite/suiteReducer.ts +++ b/packages/suite/src/reducers/suite/suiteReducer.ts @@ -456,8 +456,10 @@ export const selectIsFirmwareRevisionCheckEnabledAndFailed = ( isDeviceAcquired(device) && // If `check` is null, it means that it was not performed yet. device.authenticityChecks?.firmwareRevision?.success === false && - // If Suite is offline and we cannot perform check, the error banner shows to urge user to go online. - device.authenticityChecks.firmwareRevision.error !== 'cannot-perform-check-offline' + // If Suite is offline and cannot perform check or there is some unexpected error, an error banner is shown but Suite is otherwise unaffected. + !['cannot-perform-check-offline', 'other-error'].includes( + device.authenticityChecks.firmwareRevision.error, + ) ); }; diff --git a/packages/suite/src/support/messages.ts b/packages/suite/src/support/messages.ts index 38c19ea2a4e..b7a6399d088 100644 --- a/packages/suite/src/support/messages.ts +++ b/packages/suite/src/support/messages.ts @@ -7112,6 +7112,10 @@ export default defineMessages({ defaultMessage: "Firmware revision check couldn't be performed. Go online to verify your firmware version.", }, + TR_FIRMWARE_REVISION_CHECK_OTHER_ERROR: { + id: 'TR_FIRMWARE_REVISION_CHECK_OTHER_ERROR', + defaultMessage: 'Firmware revision check could not be performed.', + }, TR_ONBOARDING_COINS_STEP: { id: 'TR_ONBOARDING_COINS_STEP', defaultMessage: 'Activate coins',