Trezor Suite documentation

This documentation can also be found at docs.trezor.io/trezor-suite where it is available in a HTML-built version compiled using mdBook.

Repository Structure

TODO

Deployment

This chapter contains information about the release process.

Deployment

This chapter contains information about deployments, build types and production environments.

Web

Production environments

Beta (beta-wallet.trezor.io)

Domain beta-wallet.trezor.io originally hosted beta build of mytrezor wallet. With initial release of Trezor Suite this domain was decided to become entry point of closed beta program for Trezor Suite.

It has special structure:

routebasic authsourceassetPrefix
/NO@trezor/landing-page-
/wallet/startYES@trezor/landing-page-
/wallet/webYES@trezor/suite-web/wallet/web

Stable (suite.trezor.io)

Stable version (currently in public beta) is hosted on suite.trezor.io and should go live on 14th October 2020 via soft launch. Check fancy diagram how it's going to work in it's entirety.

routebasic authsourceassetPrefix
/NO@trezor/suite-web-landing-
/webNO@trezor/suite-web/web

Staging environments

Each of the above mentioned environments have its own staging environment counterpart.

Staging environments are only accessible within SatoshiLabs internal IP range (office + VPN).

productionstaging counterpart
beta-wallet.trezor.iostaging-wallet.trezor.io
suite.trezor.iostaging-suite.trezor.io

Desktop Updates

The desktop build of Trezor Suite uses an auto-updating feature to keep the application up to date with our latest published release.

Internals

The package electron-updater (part of electron-builder) is used to manage updates. We

How to publish an update

  1. Set the GH_TOKEN environment variable to a personal access token with access on the project repo scope. See Github documentation for more information.
  2. Check the version you want to publish is correct.
  3. Build all or a platform specific desktop build using yarn workspace @trezor/suite-desktop run build:desktop (all) or yarn workspace @trezor/suite-desktop run build:linux (platform specific/linux).
  4. Publish all builds or a platform specific build using yarn workspace @trezor/suite-desktop run publish:all (all) or yarn workspace @trezor/suite-desktop run publish:linux (platform specific/linux).
  5. Go to the Github Releases page, you should see a drafted release.
  6. Update the content (which will be displayed in app as a change log) and publish the release.

Versioning

We are using so-called calendar versioning in the format YY.MM.PATCH.

  • YY stands for the current year.
  • MM stands for the current month.
  • PATCH is increased on every release in the given month.

Examples

  • 20.10.1 first release in Oct 2020
  • 20.10.3 third release in Oct 2020
  • 19.12.1 first release in Dec 2019

Beta versions

We version beta in a similar way as production versions but we always set PATCH to 0 and increase the MM.

That means that every release on beta has 0 as the patch version. This has a drawback that you can't distinguish beta deployments by a version number, but beta testers should be able to read and report the commit hash.

Only stable releases have patch version >1 and this increases with each stable release: 1, 2, 3, 4.

Beta also has +1 MM version when compared to stable indicating this is upcoming release which will be deployed on stable next month.

Examples

  • 20.10.1 first release on Oct 15th to stable
  • 20.10.2 second release on Oct 22nd to stable
  • 20.11.0 release on Oct 29th 2020 to beta
  • 20.11.0 another release on Nov 5th to beta
  • 20.11.1 public release on Nov 14th to stable

Develop versioning

We use the same scheme as beta. That is, develop branch has always YY.MM.0 version where MM is the upcoming month's release. When we fork develop to release/20YY-MM branch, we bump the release branch version to YY.MM.1 and increase the develop version to YY.(MM+1).0 indicating we are already brewing next release in the develop.

Version bumping

Do not use release-it to semi-automate the version bumping. It is very aggressive and it may delete/modify some Github releases.

Versions you need to modify manually for now besides all the package.json files:

  • packages/suite-native/package.json
  • packages/suite-web/package.json
  • packages/suite/package.json

Packages

This directory contains description of various Trezor Suite packages.

Suite Desktop

Debugging (VS Code)

Using VS Code configuration files (inside .vscode), Suite Desktop can be built and run with a debugger attached to it. Running the Suite-Desktop: App task (F5) will execute all required scripts (NextJS server + Electron build) and launch the Electron app. VS Code will be set in debugging mode, allowing you, for example, to set breakpoints and inspect variables inside the electron-src folder (as well as other dependencies). For more on Debugging, please refer to the VS Code documentation.

Known issue: The devtools might blank out at launch. If this happens, simply close and re-open the devtools (CTRL + SHIFT + I).

Logging

Logging can be enabled by running Suite with the command line flag --log-level=LEVEL (replace LEVEL with error, warn, info or debug based on the logging you wish to display). Additional command line flags can be found below.

More technical information can be found on the Desktop Logger page.

Shortcuts

TODO

Runtime flags

Runtime flags can be used when running the Suite Desktop executable, enabling or disabling certain features. For example: ./Trezor-Suite-20.10.1.AppImage --disable-csp will run with this flag turned on, which will result in the Content Security Policy being disabled.

Available flags:

namedescription
--disable-cspDisables the Content Security Policy. Necessary for using DevTools in a production build.
--pre-releaseTells the auto-updater to fetch pre-release updates.
--bridge-devInstruct Bridge to support emulator (starts Bridge with -e 21324).
--log-level=NAMESet the logging level. Available levels are [name (value)]: error (1), warn (2), info(3), debug (4). All logs with a value equal or lower to the selected log level will be displayed.
--log-writeWrite log to disk
--log-no-printDisable the log priting in the console.
--log-file=FILENAMEName of the output file (defaults to log-%ts.txt)
--log-path=PATHNAMEPath for the output file (defaults to home or current working directory)
--enable-updaterEnables the auto updater (if disabled in feature flags)
--disable-updaterDisables the auto updater (if enabled in feature flags)

Features

This directory contains description of various Trezor Suite features.

Metadata (labeling)

Metadata is a feature which allows user to associate persistent data with their accounts, transactions, addresses or even hidden wallets. Trezor Suite refers to metadata as to "labeling" in user interface.

Data stores

Because Trezor Suite is not a typical application with a backend server, data must be stored elsewhere. Currently supported providers are:

  • Dropbox
  • Google Drive

Planned to be supported in future:

  • Local file system (desktop only)
  • SD card

Data structure in store

version 1.0.0 (current)

device metadata example

{
  "version": "1.0.0",
  "walletLabel": "my hidden wallet label",
},

account metadata example

{
  "version": "1.0.0",
  "accountLabel": "my cool account label",
  "outputLabels": {
    "9f472739fa7034dfb9736fa4d98915f2e8ddf70a86ee5e0a9ac0634f8c1d0007": {
      "0": "transaction 1"
    },
  },
  "addressLabels": {
    "bc1qannfxke2tfd4l7vhepehpvt05y83v3qsf6nfkk": "my cool address label",
  }
}

version 2.0.0 (future)

Each record will have timestamp which will allow user to resolve potential conflicts

Data encryption

Data is stored in encrypted form using aes-256-gcm cipher. Master key for encryption is generated by users Trezor with constants defined in suite/src/actions/suite/constants/metadataConstants.ts Files encryption-decryption logic is located in suite/src/utils/suite/metadata.ts

Where data lives in App

Settings related data is defined in suite/src/reducers/suite/metadataReducer which contains

{
 enabled: bool,
 initiating: bool,
 provider: {
   type: "dropbox" | "google",
   token: string,
   user: string
 }
}

Metadata itself is divided into 2 groups (device metadata and account metadata). They are stored together with concrete records.

Device has metadata property:

{
  /*
   - disabled is initial state. user has not interacted with metadata before, metadata keys are not available
   - enabled means that metadata is enabled for this device and application has metadata keys. App will try to download and decipher metadata
   - cancelled means that user rejected cipherKeyValue call on device. 
  */
  status: "disabled" |"enabled" | "cancelled",
  key: string,
  fileName: string,
  aesKey: string,
  walletLabel: string
}

Account has metadata property which is an object of following shape:

{
  key: string(xpub),
  fileName: string,
  aesKey: string,
  outputLabels: {
    [string(txid)]: {
      [number(outputIndex)]: string
    },
  },
  addressLabels: {
    [string(address)]: string,
  },
  accountLabel: string
}

User stories

First time user:

  1. User opens App for the first time. Metadata is disabled. "Add label" buttons are present on mouse hover over labelable data.
  2. User clicks "Add label" button.
  3. Device metadata key is generated.
  4. Using device metadata key, account metadata keys are created.
  5. Open modal with metadata providers and connect.
  6. Fetch data from metadata provider and set interval for fetching data.
  7. Activate editable input.

Metadata enabled during discovery process:

Controlled by discoveryActions

  1. If passphrase is not used device metadata key is generated before discovery process start. discoveryActions
  2. If passphrase is used metadata key is generated either:
    • in the middle of the discovery process after successful receiving first bundle of accounts and at least one the account is not empty. discoveryActions
    • after passphrase confirmation process. metadataMiddleware

How to turn metadata off

  • controls for common metadata related actions are located in general settings under the labeling section
  • there is a switch which:
    1. sets metadata.enabled bool value
    2. if setting to false, it triggers removal of all metadata (including keys) from devices and accounts.
    3. if setting to false, disconnects metadata provider (Dropbox, Google Drive)
  • there is a button "disconnect provider" which:
    1. triggers removal of all metadata values (excluding metadata keys) from devices and accounts. This way provider might be reconnected without reconnecting device
    2. disconnects metadata provider

Transactions - Export

You have the possibility to export your data in multiple formats: PDF, CSV and JSON. The export menu is available by clicking the three little dots near to the search button. Export Button

The JSON format contains the most extensive amount of information. This export can be used for data vizualisation for example.

Transactions - Search

Transactions can be searched using an input field located above the transaction list. Clicking the magnifying glass will open the input and focus in it. You can use the keyboard shortcut CTRL/⌘ + F for the same result. Search Button

You can search for the following information:

  • Transaction ID (txid)
  • Input and Output addresses
  • Output labels
  • Address labels
  • Amounts
  • Dates

All searches, apart from the two last ones (Amounts and Dates), are free text searches. No specific format is required, anything (txid, addresses, labels) matching the search will be returned.

Any numbers will be treated as amounts. For a given number, partially matching amounts will be shown. Operators, prefixed to the number, can be used for different results:

  • > will return amounts greater than the value (i.e. > 0.001 for all amounts greater than 0.001).
  • < will return amounts lower than the value (i.e. < 0 for all negative amounts).
  • = will return amounts that strictly match the value (i.e. = 0.01 will return all amounts that are exactly 0.01).
  • != will return amounts that do not match the value (i.e. != 0.01 will return all amounts that are not 0.01).

Dates provided in the format yyyy-mm-dd will return transactions matching that date. Just like amount searches, prefixed operators can be used:

  • > will return all transactions after the date, including the given date itself (i.e. > 2020-12-14 will return all transactions after December 14th, 2020).
  • < will return all transactions before the date, including the given date itself (i.e. < 2020-12-14 will return all transactions before December 14th, 2020).
  • != will return all transactions that are not matching the date (i.e. != 2020-12-14 will return all transactions except the ones on December 14th, 2020).

Multiple fields can be searched at the same time using AND (&) and OR (|) operators. A few examples using these operators might be pretty self-explanatory:

  • > 2020-12-01 & < 2020-12-31 will return all transactions from December 2020.
  • > 2020-12-01 & < 2020-12-31 & > 0 will return all incoming (positive) transactions from December 2020.
  • > 0.01 | > 2020-12-01 will return all transactions with an amount higher than 0.01 OR that have happened since December 1st, 2020.
  • > 2019-12-01 & < 2019-12-31 | > 2020-12-01 & < 2020-12-31 will return all transactions from December 2020 and 2019.

As you may see in the last example, and similarly to SQL, the AND (&) operator has precedence over the OR (|) operator.

Tests

This chapter contains information about test.

Suite Web e2e tests

Suite uses Cypress to run e2e tests.

Trezor-user-env

Trezor-user-env is a docker image which provides all the necessary instrumentation required to run test (bridge and emulators).

run_tests script

The run_tests.js script is the entrypoint for e2e tests. It:

  • picks tests files to be run (see @tags)
  • retries tests if needed (see @retry)
  • reports tests results

tags

Each test should be assigned a tag at the top of the test file. These allow you to add more fine grained control in run_tests.js.

At the moment, there are following tags:

  • @group:[string]
  • @retry=[number]

@group

Assigning a @group allows run_tests.js script to sort the test files into groups and run them in parallel on CI. At the moment these groups exist:

  • @group:metadata
  • @group:device-management
  • @group:suite
  • @group:onboarding
  • @group:settings

@retry

If there is a test that you for any reason need to retry if it fails you may provide @retry=2 tag. In this case, test will be ran 3 times in total and count as failed only if all runs fail.

Results

There is a tool to track tests runs and their results, temporarily hosted here https://track-suite.herokuapp.com/ Repo here: https://github.com/mroz22/track-suite

Debug tests locally

Suite test

Suite test opens cypress test runner and prepares everything to run tests.

./docker/docker-suite-test.sh

Disclaimer: GUI from docker container does not work on mac. Only linux is supported at the moment.

Image snapshots

It is possible to run tests with image snapshots to test for visual regressions. To enable snapshots, use env variable:

CYPRESS_SNAPSHOT=1 ./docker/docker-suite-test.sh

When you need to update image snapshots you have 2 options:

  • use CI job. This will generate new snapshots in artifacts together with a handy script that updates your snapshots locally. Check the log output.
  • use ./docker/docker-suite-snapshots.sh. This does the same as ./docker/docker-suite-test.sh, the only difference is it won't fail on non-matching snapshots but generate new snapshots.

Old notes (to be removed)

Notes on bridge (trezord)

Cypress has 2 problems when communicating with trezord.

  1. Does not send request origin headers correctly -> problem with CORS.
  2. Tests in CI fail occasionally on POST to '/' with 403 -> problem with CSFR.
  3. Change default address to 0.0.0.0 instead of 127.0.0.1 to make it work on mac without any other configuration

As a workaround we have a custom binary compiled from this trezord branch

Assorted knowledge

This directory serves as a dumping ground for important knowledge tidbits that do not clearly fit in any particular location. Please add any information that you think should be written down.

At any time, information stored here might be restructured or moved to a different location, so as to ensure that the documentation is well structured overall.

Analytics

Suite collects anonymous data on how user interacts with the application. This feature is by default "on". User has to opt-out either on analytics screen that follows after welcome screen (if starting Suite for the first time) or by unchecking switch in settings.

Data is transferred in GET requests encoded in uri.

Catalogue of events

Refer to AnalyticsEvent type in analyticsActions.ts

Anonymity

Suite should never collect:

  • device id
  • any fingerprinting

Data endpoints

List of available configured endpoints:

https://data.trezor.io/suite/log    /desktop   /staging     .log
https://data.trezor.io/suite/log    /desktop   /beta        .log
https://data.trezor.io/suite/log    /desktop   /develop     .log
https://data.trezor.io/suite/log    /desktop   /stable      .log
https://data.trezor.io/suite/log    /web       /staging     .log
https://data.trezor.io/suite/log    /web       /beta        .log
https://data.trezor.io/suite/log    /web       /develop     .log
https://data.trezor.io/suite/log    /web       /stable      .log

Currently used endpoints:

envAre
staging-suite.trezor.iohttps://data.trezor.io/suite/log/web/staging.log
beta-wallet.trezor.iohttps://data.trezor.io/suite/log/web/beta.log
suite.trezor.iohttps://data.trezor.io/suite/log/web/stable.log
any originhttps://data.trezor.io/suite/log/web/develop.log
desktophttps://data.trezor.io/suite/log/desktop/beta.log

Versioning

Whenever there shall be a change in AnalyticsEvent type version variable in analyticsActions.ts should be bumped. Please follow simple semver versioning in format <breaking-change>.<analytics-extended>. Breaking change should bump major version. Any other change bumps minor version.

Changelog

1.6 (unreleased)

Added:

  • suite-ready
    • suiteVersion: string | ""
  • device-connect
    • isBitcoinOnly: boolean
  • desktop-init
    • desktopOSVersion: string | "" (in format: {platform}_{release})
  • accounts/empty-account/buy
    • symbol: string
  • account-create
    • tokensCount: number
  • transaction-created
    • action: 'sent' | 'copied' | 'downloaded'
    • symbol: string
    • broadcast: boolean
    • outputsCount: number
    • bitcoinRbf: boolean
    • bitcoinLockTime: boolean
    • ethereumData: boolean
    • tokenSent: boolean
  • add-token
    • networkSymbol: string
    • addedNth: number

1.5

Added:

  • suite-ready
    • theme (dark mode)
  • wallet/created
    • type: standard | hidden
  • device-disconnect

1.4

Added:

  • suite-ready
    • rememberedStandardWallets
    • rememberedHiddenWallets
  • analytics/enable
  • analytics/dispose
  • check-seed/error
  • check-seed/success

1.3

Added:

  • device-connect
    • backup_type
  • router/location-change
    • prevRouterUrl
    • nextRouterUrl

1.2

Added

  • suite-ready
    • tor

1.1

Added:

  • device-update-firmware:
    • toFwVersion
  • suite-ready
    • platformLanguage
    • platform
  • device-connect:
    • totalInstances

1.0

  • initial version

Desktop Logger

The desktop application includes a logging library to display various types of log in the console or in a file.

Four (or five if we count 'mute') log levels are currently implemented:

  • error (1)
  • warn (2)
  • info (3)
  • debug (4)

All messages with an inferior level to the selected one will be displayed. For example, if the selected log level is info, then it will also display warn and error messages.

How to enable logging

Logging can be enabled by running Suite with the command line flag --log-level=LEVEL (replace LEVEL with error, warn, info or debug based on the logging you wish to display). Additional command line flags can be found on the Suite-Desktop page.

API

Exported Types

LogLevel

Any of the following values:

  • mute (0)
  • error (1)
  • warn (2)
  • info (3)
  • debug (4)

Options (all optional)

nametypedefault valuedescription
colorsbooleantrueConsole output has colors
writeToConsolebooleantrueOutput is displayed in the console
writeToDiskbooleanfalseOutput is written to a file
outputFilestring'log-%ts.txt'file name for the output
outputPathstringHome or CWDpath for the output
logFormatstring'%dt - %lvl(%top): %msg'Output format of the log

String formatters

The options outputFile and logFormat can be used with some expressions, prefixed with the percent (%) symbol, to apply certain dynamic elements. It is for example possible to display a timestamp or the current date & time in the listed options above. While some expressions can be used in any strings, some strings have their own expressions.

Global

ExpressionExample outputDescription
%ts1611054460306Timestamp
%dt2021-01-19T11:08:22.244ZDate and time in ISO format (ISO 8601)

logFormat

ExpressionExample outputDescription
%lvlINFOLevel in letters and upper case
%topExampleTopic
%msgExample messageMessage

Constructor

The constructor has the following parameters:

  • level (LogLevel): Selected log level (see LogLevels in Exported Types above)
  • options (Options): Optional parameter containing settings for the logger (see Options in Exported Types above)

Log methods

All log methods have the same signature as they are just wrappers around a private logging method.

The following methods are available:

  • error(topic: string, messages: string | string[]); // level: 1
  • warn(topic: string, messages: string | string[]); // level: 2
  • info(topic: string, messages: string | string[]); // level: 3
  • debug(topic: string, messages: string | string[]); // level: 4

Parameters:

  • topic (string): Message topic
  • messages (string | string[]): Single message or array of messages which will be displayed one by line.

Exit method

The exit() method is used to close the write stream of the log file. If you are not planning to write logs to disk, you won't need to use this method. Otherwise it is highly advised to place this inside exit/crash callbacks.

Example

const logger = new Logger('warn', {
    colors: false, // Turning off colors
    logFormat: '%lvl: %msg', // Level and message only
    writeToDisk: true, // Write to disk
    outputFile: 'log-desktop.txt', // Static file name, will be overwritten if it exists
});

// These messages will be printed with the level provided above
logger.error('example', 'This is an example error message with the topic "example"');
logger.warn('example', 'This is an example warn message with the topic "example"');

// And these won't
logger.info('example', 'This is an example info message with the topic "example"');
logger.debug('example', 'This is an example debug message with the topic "example"');

// Closes the write stream (only needed of writing the log file)
logger.exit();

Q&A

How to format a string?

The library does not have any formatting capabilities as JavaScript already has templating features built-in. Simply use string literals (for example: logger.info('Topic', `My string ${myvar}.`)).

How to output an object?

The library does not include any helper for this as there is already a language feature that does this. Simply use JSON.stringify(myObject).

How can I write the log in JSON format?

You can change the logOutput option to be formatted like JSON. For example: logOutput: '{ "ts": "%ts", "lvl": "%lvl", "topic": "%top", "message": "%msg" },'. In order to display/use it properly, you will have to edit the output a little bit. Wrap all the messages in square brakets ([, ]) and remove the comma (,) from the last message.

Feature Flags

Feature flags allow us to enable and disable certain features at build time, helping us to produce specific builds for specific environments.

Work flow

All feature flags are located in packages/suite/config/features.ts. To add a new flag, start by doing the following:

  1. Add your flag to the FLAGS constant and set its defautl value. When naming your flag, bear in mind the following conventions:
    1. Always explain what the flag is about using a comment next to it.
    2. The name of the flag should always be in capitals.
    3. The name of the flag should never contain the world enable or disable because the name should always towards an enabled state. Its value should reflect whether the feature is enabled or not.
    4. The name of the flag should never contain the word flag because it's inferred.
  2. (optional) You can override the flag for each environment (web, desktop, landing) using their specific constants.
  3. Use the isEnabled function from @suite-utils/features to check if the flag is enabled or not.
  4. Wrap the code you wish to control in a condition checking for your flag being enabled or not.

Example

import { isEnabled } from '@suite-utils/features';

const main = () => {
    alwaysRunning();

    if (isEnabled('LABELING')) {
        myLabelingFeature();
    }
};

Future evolutions

  • Control feature flags at runtime.

Review Process

Same as for Trezor Firmware. Please see here.

Weird stuff, notes and issues

Things to do in future

Bridge in electron

  • package.json > "build": { "asar": false } required
  • How to get bridge version?
  • Test windows

Debugging Electron build

  • macOS: Run /path/to/app/TrezorSuite.app/Contents/MacOS/TrezorSuite --debug
  • Decompile: npx asar extract packages/suite-desktop/build-electron/mac/TrezorSuite.app/Contents/Resources/app.asar ./decompiled

Remove IndexedDB from desktop

To remove a database remove following folder:

Windows

C:\Users\<user>\AppData\Roaming\@trezor\suite-desktop\IndexedDB

Linux

/home/<user>/.config/@trezor/suite-desktop/IndexedDB

macOS

/Users/<user>/Library/Application Support/@trezor/suite-desktop/IndexedDB

Clearing Electron cache

To clear electron cache delete following folder:

Windows

C:\Users\<user>\AppData\Roaming\@trezor\suite-desktop\Cache

Linux

/home/<user>/.config/@trezor/suite-desktop/Cache

macOS

/Users/<user>/Library/ApplicationSupport/@trezor/suite-desktop/Cache

Tests

React-native tsconfig regex:

copy block from top level tsconfig.json find: ./packages/suite/src/(.*)" replace: ./src/$1", "../../packages/suite/src/$1"

Debugging suite-web on android

Server needs to be running on https in order to have access to navigator.usb functionality

  • Generate localhost certificate: yarn workspace @trezor/suite-web cert

  • Run https server: yarn workspace @trezor/suite-web dev:https

  • Find your ip: ifconfig | grep "inet "

  • Connect phone (dev mode) to computer

  • Access suite using IP (it needs to be in the same network as your computer)

  • Open debugger: chrome://inspect/#devices

How to release - staging

git checkout releases

yarn

Landing page release

  1. yarn workspace @trezor/landing-page build
  2. cd packages/landing-page
  3. ./scripts/s3sync.sh stage beta

Suite web release

  1. assetPrefix=/wallet yarn workspace @trezor/suite-web build
  2. cd packages/suite-web
  3. cd build/static
  4. mkdir desktop
  5. copy desktop apps into the folder (Trezor Suite.(zip, AppImage, exe)).

Image of Yaktocat

  1. ./scripts/s3sync.sh stage beta (from the packages/suite-web folder )

Upload source maps to Sentry

  1. sentry-cli releases -o satoshilabs -p trezor-suite files <COMMIT> upload-sourcemaps ./build