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:
route | basic auth | source | assetPrefix |
---|---|---|---|
/ | NO | @trezor/landing-page | - |
/wallet/start | YES | @trezor/landing-page | - |
/wallet/web | YES | @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.
route | basic auth | source | assetPrefix |
---|---|---|---|
/ | NO | @trezor/suite-web-landing | - |
/web | NO | @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).
production | staging counterpart |
---|---|
beta-wallet.trezor.io | staging-wallet.trezor.io |
suite.trezor.io | staging-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
- Set the
GH_TOKEN
environment variable to a personal access token with access on the project repo scope. See Github documentation for more information. - Check the version you want to publish is correct.
- Build all or a platform specific desktop build using
yarn workspace @trezor/suite-desktop run build:desktop
(all) oryarn workspace @trezor/suite-desktop run build:linux
(platform specific/linux). - Publish all builds or a platform specific build using
yarn workspace @trezor/suite-desktop run publish:all
(all) oryarn workspace @trezor/suite-desktop run publish:linux
(platform specific/linux). - Go to the Github Releases page, you should see a drafted release.
- 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 202020.10.3
third release in Oct 202019.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 stable20.10.2
second release on Oct 22nd to stable20.11.0
release on Oct 29th 2020 to beta20.11.0
another release on Nov 5th to beta20.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:
name | description |
---|---|
--disable-csp | Disables the Content Security Policy. Necessary for using DevTools in a production build. |
--pre-release | Tells the auto-updater to fetch pre-release updates. |
--bridge-dev | Instruct Bridge to support emulator (starts Bridge with -e 21324 ). |
--log-level=NAME | Set 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-write | Write log to disk |
--log-no-print | Disable the log priting in the console. |
--log-file=FILENAME | Name of the output file (defaults to log-%ts.txt ) |
--log-path=PATHNAME | Path for the output file (defaults to home or current working directory) |
--enable-updater | Enables the auto updater (if disabled in feature flags) |
--disable-updater | Disables 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:
- User opens App for the first time. Metadata is disabled. "Add label" buttons are present on mouse hover over labelable data.
- User clicks "Add label" button.
- Device metadata key is generated.
- Using device metadata key, account metadata keys are created.
- Open modal with metadata providers and connect.
- Fetch data from metadata provider and set interval for fetching data.
- Activate editable input.
Metadata enabled during discovery process:
Controlled by discoveryActions
- If passphrase is not used device metadata key is generated before discovery process start.
discoveryActions
- 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
- in the middle of the discovery process after successful receiving first bundle of accounts and at least one the account is not empty.
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:
- sets metadata.enabled bool value
- if setting to false, it triggers removal of all metadata (including keys) from devices and accounts.
- if setting to false, disconnects metadata provider (Dropbox, Google Drive)
- there is a button "disconnect provider" which:
- triggers removal of all metadata values (excluding metadata keys) from devices and accounts. This way provider might be reconnected without reconnecting device
- 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.
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.
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.
- Does not send request origin headers correctly -> problem with CORS.
- Tests in CI fail occasionally on POST to '/' with 403 -> problem with CSFR.
- 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:
env | Are |
---|---|
staging-suite.trezor.io | https://data.trezor.io/suite/log/web/staging.log |
beta-wallet.trezor.io | https://data.trezor.io/suite/log/web/beta.log |
suite.trezor.io | https://data.trezor.io/suite/log/web/stable.log |
any origin | https://data.trezor.io/suite/log/web/develop.log |
desktop | https://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)
name | type | default value | description |
---|---|---|---|
colors | boolean | true | Console output has colors |
writeToConsole | boolean | true | Output is displayed in the console |
writeToDisk | boolean | false | Output is written to a file |
outputFile | string | 'log-%ts.txt' | file name for the output |
outputPath | string | Home or CWD | path for the output |
logFormat | string | '%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
Expression | Example output | Description |
---|---|---|
%ts | 1611054460306 | Timestamp |
%dt | 2021-01-19T11:08:22.244Z | Date and time in ISO format (ISO 8601) |
logFormat
Expression | Example output | Description |
---|---|---|
%lvl | INFO | Level in letters and upper case |
%top | Example | Topic |
%msg | Example message | Message |
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:
- Add your flag to the
FLAGS
constant and set its defautl value. When naming your flag, bear in mind the following conventions:- Always explain what the flag is about using a comment next to it.
- The name of the flag should always be in capitals.
- The name of the flag should never contain the world
enable
ordisable
because the name should always towards an enabled state. Its value should reflect whether the feature is enabled or not. - The name of the flag should never contain the word
flag
because it's inferred.
- (optional) You can override the flag for each environment (web, desktop, landing) using their specific constants.
- Use the
isEnabled
function from@suite-utils/features
to check if the flag is enabled or not. - 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
-
Typing connected component using redux-thunk action creators
-
Tests for custom hooks in suite are ignored (modulePathIgnorePatterns: '
/src/utils/suite/hooks')
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
- All suite tests use UTC timezone (set in jest.config.js#L2)
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
yarn workspace @trezor/landing-page build
cd packages/landing-page
./scripts/s3sync.sh stage beta
Suite web release
assetPrefix=/wallet yarn workspace @trezor/suite-web build
cd packages/suite-web
cd build/static
mkdir desktop
- copy desktop apps into the folder (Trezor Suite.(zip, AppImage, exe)).
./scripts/s3sync.sh stage beta
(from thepackages/suite-web
folder )
Upload source maps to Sentry
sentry-cli releases -o satoshilabs -p trezor-suite files <COMMIT> upload-sourcemaps ./build