diff --git a/.github/workflows/build_and_functional_tests.yml b/.github/workflows/build_and_functional_tests.yml new file mode 100644 index 0000000..c92fc21 --- /dev/null +++ b/.github/workflows/build_and_functional_tests.yml @@ -0,0 +1,32 @@ +name: Build and run functional tests using ragger through reusable workflow + +# This workflow will build the app and then run functional tests using the Ragger framework upon Speculos emulation. +# It calls a reusable workflow developed by Ledger's internal developer team to build the application and upload the +# resulting binaries. +# It then calls another reusable workflow to run the Ragger tests on the compiled application binary. +# +# While this workflow is optional, having functional testing on your application is mandatory and this workflow and +# tooling environment is meant to be easy to use and adapt after forking your application + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + build_application: + name: Build application using the reusable workflow + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_build.yml@v1 + with: + upload_app_binaries_artifact: "compiled_app_binaries" + + ragger_tests: + name: Run ragger tests using the reusable workflow + needs: build_application + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_ragger_tests.yml@v1 + with: + download_app_binaries_artifact: "compiled_app_binaries" diff --git a/.github/workflows/codeql_checks.yml b/.github/workflows/codeql_checks.yml new file mode 100644 index 0000000..edcc7cf --- /dev/null +++ b/.github/workflows/codeql_checks.yml @@ -0,0 +1,43 @@ +name: "CodeQL" + +on: + workflow_dispatch: + push: + branches: ["main", "master", "develop"] + pull_request: + branches: ["main", "master", "develop"] + # Excluded path: add the paths you want to ignore instead of deleting the workflow + paths-ignore: + - '.github/workflows/*.yml' + - 'tests/*' + +jobs: + analyse: + name: Analyse + strategy: + matrix: + sdk: [ "$NANOS_SDK", "$NANOX_SDK", "$NANOSP_SDK", "$STAX_SDK", "$FLEX_SDK" ] + #'cpp' covers C and C++ + language: [ 'cpp' ] + runs-on: ubuntu-latest + container: + image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-legacy:latest + + steps: + - name: Clone + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + queries: security-and-quality + + # CodeQL will create the database during the compilation + - name: Build + run: | + make BOLOS_SDK=${{ matrix.sdk }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.gitignore b/.gitignore index f3f7d18..05bf995 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,10 @@ +build bin debug -dep -obj -src/glyphs.c -src/glyphs.h .idea -nano-sdk -blue-sdk + +# Temporary directory with snapshots taken during test runs +tests/snapshots-tmp/ + +# Output folder of clang static analyzer scan-build, see Makefile.rules-generic in Ledger SDK +output-scan-build diff --git a/Makefile b/Makefile index 23c545a..a2d5a9c 100755 --- a/Makefile +++ b/Makefile @@ -18,139 +18,113 @@ ifeq ($(BOLOS_SDK),) $(error Environment variable BOLOS_SDK is not set) endif + include $(BOLOS_SDK)/Makefile.defines -APPNAME = Nimiq -APP_LOAD_PARAMS=--appFlags 0x240 --path "44'/242'" --curve ed25519 $(COMMON_LOAD_PARAMS) - -APPVERSION_M=1 -APPVERSION_N=4 -APPVERSION_P=6 -APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) - -#prepare hsm generation -ifeq ($(TARGET_NAME),TARGET_BLUE) -ICONNAME=blue_app_nimiq.gif -else ifeq ($(TARGET_NAME),TARGET_NANOS) -ICONNAME=nanos_app_nimiq.gif -else -ICONNAME=nanox_app_nimiq.gif -endif -################ -# Default rule # -################ -all: default +############################## +# App description # +############################## -############ -# Platform # -############ +# Application name +APPNAME = Nimiq -DEFINES += OS_IO_SEPROXYHAL -DEFINES += HAVE_BAGL HAVE_SPRINTF HAVE_SNPRINTF_FORMAT_U -DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=4 IO_HID_EP_LENGTH=64 HAVE_USB_APDU -DEFINES += LEDGER_MAJOR_VERSION=$(APPVERSION_M) LEDGER_MINOR_VERSION=$(APPVERSION_N) LEDGER_PATCH_VERSION=$(APPVERSION_P) +# Application version +APPVERSION_M = 2 +APPVERSION_N = 0 +APPVERSION_P = 0 +APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" + +# Setting to allow building variant applications. For now, there are no variants, only the main Nimiq app. +VARIANT_PARAM = COIN +VARIANT_VALUES = nimiq + + +############################## +# Source files # +############################## + +# Application source files +APP_SOURCE_PATH += src + +# For now, don't include standard app files, as our code is not currently using the new common / standard app utilities, +# with the advantage of not including any utilities we don't use, but of the downside of having to maintain these common +# code utilities ourselves. +DISABLE_STANDARD_APP_FILES = 1 + +# Application icons following the guidelines: +# https://developers.ledger.com/docs/device-app/deliver/deliverables/icons +# Notes on how to create such icons in GIMP: +# - Open an SVG of the Nimiq logo and set the image width to the size of the safe area for the intended image size, e.g. +# 30px for the 32pxx32px image. Also import the paths. +# - Layer > Transparency > Remove Alpha Channel. +# - Edit > Fill the image with white. +# - Edit > Fill Path with black, or Select > From Path and fill the selection with a radial gradient, potentially with +# an adequate offset set. +# - Set Image > Mode to indexed and for 1bit-per-pixel images (Nano devices) use a color palette with just black and +# white, and for 4bit-per-pixel images (e-ink devices) use a color palette with 16 colors 0x00, 0x11, 0x22, ..., 0xff. +# If desired, enable dithering and experiment with different modes. Mode "positioned" looks quite nice. For more +# control, also Colors > Dither might be used. +# - Set the Layer > Layer Boundary Size to the intended size of the final image, centering the content. Then Image > Fit +# Canvas to Layers. +# - Save the icon as xcf with the indexed color map which can be used for future changes to the image. +# - For binary black/white icons: export the icon with indexed color mode as gif, which will save as 1bit-per-pixel. +# For grayscale icons: The guidelines_enforcer.yml workflow expects grayscale images to have 8bit-per-pixel depth +# (even though the glyph build task seems to be compatible with lower bit-per-pixel), therefore change the Image > +# Mode to Grayscale and then export as gif. Do not overwrite the xcf file, to keep it in indexed mode. +# For some reason, for glyphs/app_nimiq_64px.gif, but not for the other icons, this made the glyph build task complain +# that the image is not indexed. So not quite sure yet, what the proper procedure would be here. Also tried, re-export +# of app_nimiq_64px.gif via https://www.photopea.com/, which exports with a color map with 256 entries, excess entries +# simply being filled up as black, but that still had 4bpp depth the enforcer complained about. Switching to Grayscale +# and then back to an indexed palette with 32 shades of gray seems to have done the trick. +# Note that for checking for the bit depth, ImageMagick's identify can be used: identify -verbose /path/to/icon.gif +ICON_NANOS = icons/app_nimiq_16px.gif +ICON_NANOX = icons/app_nimiq_14px.gif +ICON_NANOSP = icons/app_nimiq_14px.gif +ICON_STAX = icons/app_nimiq_32px.gif +ICON_FLEX = icons/app_nimiq_40px.gif + + +############################## +# Permissions and features # +############################## + +# Application allowed derivation curves. +CURVE_APP_LOAD_PARAMS = ed25519 + +# Application allowed derivation paths. +PATH_APP_LOAD_PARAMS = "44'/242'" + +# See SDK `include/appflags.h` for the purpose of each permission +HAVE_APPLICATION_FLAG_GLOBAL_PIN = 1 +#HAVE_APPLICATION_FLAG_BOLOS_SETTINGS = 1 # Automatically set in Makefile.standard_app for devices with bluetooth + +ENABLE_BLUETOOTH = 1 +ENABLE_NBGL_QRCODE = 1 # U2F +# U2F has been deprecated by Ledger, disabled in most apps and is not configured in $(BOLOS_SDK)/Makefile.standard_app, +# but we keep it for now as Nimiq is a browser centric coin, and Firefox and Safari don't support WebHID or WebUSB yet. +# Note though that U2F is somewhat memory hungry, apart from the significant UX flaws. Also see: +# https://www.ledger.com/windows-10-update-sunsetting-u2f-tunnel-transport-for-ledger-devices +# https://developers.ledger.com/docs/connectivity/ledgerJS/faq/U2F DEFINES += HAVE_IO_U2F DEFINES += U2F_PROXY_MAGIC=\"w0w\" DEFINES += U2F_REQUEST_TIMEOUT=28000 # 28 seconds -DEFINES += USB_SEGMENT_SIZE=64 -DEFINES += BLE_SEGMENT_SIZE=32 #max MTU, min 20 -DEFINES += UNUSED\(x\)=\(void\)x -DEFINES += APPVERSION=\"$(APPVERSION)\" - -#WEBUSB_URL = www.ledgerwallet.com -#DEFINES += HAVE_WEBUSB WEBUSB_URL_SIZE_B=$(shell echo -n $(WEBUSB_URL) | wc -c) WEBUSB_URL=$(shell echo -n $(WEBUSB_URL) | sed -e "s/./\\\'\0\\\',/g") -DEFINES += HAVE_WEBUSB WEBUSB_URL_SIZE_B=0 WEBUSB_URL="" - -ifeq ($(TARGET_NAME),TARGET_NANOX) -DEFINES += HAVE_BLE BLE_COMMAND_TIMEOUT_MS=2000 -DEFINES += HAVE_BLE_APDU # basic ledger apdu transport over BLE -endif - -ifeq ($(TARGET_NAME),TARGET_NANOS) -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=128 -else -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300 -DEFINES += HAVE_GLO096 -DEFINES += HAVE_BAGL BAGL_WIDTH=128 BAGL_HEIGHT=64 -DEFINES += HAVE_BAGL_ELLIPSIS # long label truncation feature -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_REGULAR_11PX -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_EXTRABOLD_11PX -DEFINES += HAVE_BAGL_FONT_OPEN_SANS_LIGHT_16PX -DEFINES += HAVE_UX_FLOW -endif - - -# Enabling debug PRINTF -DEBUG = 0 -ifneq ($(DEBUG),0) - - ifeq ($(TARGET_NAME),TARGET_NANOS) - DEFINES += HAVE_PRINTF PRINTF=screen_printf - else - DEFINES += HAVE_PRINTF PRINTF=mcu_usb_printf - endif -else - DEFINES += PRINTF\(...\)= -endif - - - -############## -# Compiler # -############## -ifneq ($(BOLOS_ENV),) -$(info BOLOS_ENV=$(BOLOS_ENV)) -CLANGPATH := $(BOLOS_ENV)/clang-arm-fropi/bin/ -GCCPATH := $(BOLOS_ENV)/gcc-arm-none-eabi-5_3-2016q1/bin/ -else -$(info BOLOS_ENV is not set: falling back to CLANGPATH and GCCPATH) +SDK_SOURCE_PATH += lib_u2f + +# Enabling DEBUG flag will enable PRINTF and disable optimizations +# Instead of setting it here you can also enable this flag during compilation via `make DEBUG=1` +#DEBUG = 1 +# Make the debug flag available in the code as NIMIQ_DEBUG preprocessor define. +ifneq ($(DEBUG), 0) +DEFINES += NIMIQ_DEBUG endif -ifeq ($(CLANGPATH),) -$(info CLANGPATH is not set: clang will be used from PATH) -endif -ifeq ($(GCCPATH),) -$(info GCCPATH is not set: arm-none-eabi-* will be used from PATH) -endif - -CC := $(CLANGPATH)clang - -#CFLAGS += -O0 -CFLAGS += -O3 -Os - -AS := $(GCCPATH)arm-none-eabi-gcc - -LD := $(GCCPATH)arm-none-eabi-gcc -LDFLAGS += -O3 -Os -LDLIBS += -lm -lgcc -lc - -# import rules to compile glyphs(/pone) -include $(BOLOS_SDK)/Makefile.glyphs - -### computed variables -APP_SOURCE_PATH += src -SDK_SOURCE_PATH += lib_stusb -SDK_SOURCE_PATH += lib_stusb_impl -SDK_SOURCE_PATH += lib_u2f -SDK_SOURCE_PATH += lib_ux - -ifeq ($(TARGET_NAME),TARGET_NANOX) -SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl -endif - -load: all - python -m ledgerblue.loadApp $(APP_LOAD_PARAMS) - -delete: - python -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS) - -# import generic rules from the sdk -include $(BOLOS_SDK)/Makefile.rules -#add dependency on custom makefile filename -dep/%.d: %.c Makefile.genericwallet +# Extend the base Makefile for standard apps +include $(BOLOS_SDK)/Makefile.standard_app -listvariants: - @echo VARIANTS COIN nimiq +# Makes a detailed report of code and data size in debug/size-report.txt +# More useful for production builds with DEBUG=0 +size-report: bin/app.elf + arm-none-eabi-nm --print-size --size-sort --radix=d bin/app.elf >debug/size-report.txt diff --git a/README.md b/README.md index 2275658..d0d8482 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,27 @@ -# Nimiq app for the Ledger Nano S and Ledger Blue +# Nimiq app for Ledger devices ## Introduction -This is a wallet app for the [Ledger Nano S](https://www.ledgerwallet.com/products/ledger-nano-s) and [Ledger Blue](https://www.ledgerwallet.com/products/ledger-blue) that makes it possible to store your [Nimiq](https://nimiq.com/)-based assets on those devices. +This is a device app for the [Ledger Hardware Wallets](https://www.ledger.com/) which makes it possible to store your +[Nimiq token (NIM)](https://nimiq.com/) on those devices (more precisely use the associated private key for NIM related +operations). -A companion [Javascript library](https://github.com/LedgerHQ/ledgerjs) is available to communicate with this app. +A companion [Javascript library](https://github.com/nimiq/ledger-api) is available to communicate with this app. -To learn how to use this library you can take look at the [demo project](https://github.com/jeffesquivels/ledgerjs-nimiq). +## Development Setup -## Building and installing - -To build and install the app on your Nano S or Blue you must set up the Ledger Nano S or Blue build environment. Please follow the Getting Started instructions at the [Ledger Nano S github repository](https://github.com/LedgerHQ/ledger-nano-s). - -Alternatively, you can set up the Vagrant Virtualbox Ledger environment maintained [here](https://github.com/fix/ledger-vagrant). This sets up an Ubuntu virtual machine with the Ledger build environment already set up. Note that if you are on a Mac, at the time of this writing this seems to be the only way to build and load the app. - -The command to compile and load the app onto the device is: - -```$ make load``` - -To remove the app from the device do: - -```$ make delete``` +For build and installation instructions, please refer to: +- The README of the [Ledger Boilerplate App](https://github.com/LedgerHQ/app-boilerplate) +- [Usage instructions of Ledger's VSCode extension](https://marketplace.visualstudio.com/items?itemName=LedgerHQ.ledger-dev-tools) + and the associated [quickstart guide](https://developers.ledger.com/docs/device-app/beginner/vscode-extension), + or the underlying [ledger-app-builder docker images](https://github.com/LedgerHQ/ledger-app-builder/), if you prefer + to use them directly, without the VSCode extension. Here at Nimiq, mainly the docker images have been used directly + for building and testing the application via the [Speculos](https://github.com/LedgerHQ/speculos) emulator, on real + devices and via [Ragger](https://github.com/LedgerHQ/ragger) functional tests. ## Testing -The `./test` directory contains files for testing the transaction parser and some printing utilities. To build and execute the tests run `./test.sh`. - -## Key pair validation +The project contains functional tests powered by [Ragger](https://github.com/LedgerHQ/ragger). They can be launched via +the VSCode extension or docker images as described in the section [Development Setup](#development-setup). -The operation to retrieve the public key implements an optional keypair verification method. Along with the request to retrieve the public key a small message is sent that is to be signed by the device. Back on the host the returned signature can be checked against the returned public key. This is to guard against incompatibility between the keypairs generated by the Ledger device and the ones expected by the Nimiq network, whatever the reason for this might be. The extra precaution prevents users from sending funds to an address they are not able to sign transactions for. +Additionally, the project contains [unit-tests](https://github.com/nimiq/ledger-app-nimiq/tree/master/unit-tests). diff --git a/ble_app_nimiq.gif b/ble_app_nimiq.gif deleted file mode 100644 index 0c5d5f3..0000000 Binary files a/ble_app_nimiq.gif and /dev/null differ diff --git a/doc/nimiqapp.md b/doc/nimiqapp.md index 61e8460..4f4ad19 100644 --- a/doc/nimiqapp.md +++ b/doc/nimiqapp.md @@ -4,196 +4,246 @@ This document describes the APDU messages interface to communicate with the Nimiq application. -The application covers the following functionalities on ed25519: +The application covers the following functionalities: - - Retrieve a public Nimiq address given a BIP 32 path - - Sign a single Nimiq basic transaction given a BIP 32 path and the transaction serialized content + - Retrieve a Nimiq public key + - Sign Nimiq transaction + - Sign messages in the Nimiq format -The application interface can be accessed over HID or BLE +## Transport protocol -## General purpose APDUs +### General transport description -### Get Public Key +Ledger APDU requests and responses are encapsulated using a flexible protocol allowing to fragment large payloads over +different underlying transport mechanisms. -#### Description +The common transport header is defined as follows: -This command returns the public key for the given BIP 32 path. An optional message can be sent to sign to verify -the validity of the generated keypair. Optionally, the address can also be showed to the user for confirmation. +| *Description* | *Length* | +|---------------------------------------|----------| +| Communication channel ID (big endian) | 2 | +| Command tag | 1 | +| Packet sequence index (big endian) | 2 | +| Payload | variable | -#### Coding +The Communication channel ID allows command multiplexing over the same physical link. It is not used for the time +being, and should be set to 0101 to avoid compatibility issues with implementations ignoring a leading 00 byte. -**Command** +The Command tag describes the message content. Use TAG_APDU (0x05) for standard APDU payloads, or TAG_PING (0x02) for a +simple link test. -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* | -|-------|--------|--------------------|------------|----------|------| -| E0 | 02 | 00 : don't return signature | 00 : don't ask for confirmation | | | -| | | 01 : return signature | 01 : ask for confirmation | | | +The Packet sequence index describes the current sequence for fragmented payloads. The first fragment index is 0x00. -**Input data** +Note that the handling of the low level transport protocol is handled by BOLOS / the SDK. The Nimiq app operates on the +APDU protocol on top of the low level transport protocol, see next section. -| *Description* | *Length* | -|-----------------------------------------------------------------------------------|--------------| -| Number of BIP 32 derivations to perform (max 10) | 1 | -| First derivation index (big endian) | 4 | -| ... | 4 | -| Last derivation index (big endian) | 4 | -| Message to sign | var (max 32) | +### Application Protocol Data Unit (APDU) protocol -**Output data** +The communication protocol used by [BOLOS](https://ledger.readthedocs.io/en/latest/bolos/overview.html) to exchange +[APDUs](https://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit) is very close to +[ISO 7816-4](https://www.iso.org/standard/77180.html) with a few differences: -| *Description* | *Length* | -|-----------------------------------------------------------------------------------|----------| -| Public Key | 32 | -| Signature if requested | 64 | +- `Lc` length is always exactly 1 byte, i.e. payload length is limited to 255 bytes. +- No `Le` field in APDU command +- The maximum size of APDU commands and responses, including payload and encoding, is defined by IO_APDU_BUFFER_SIZE in + the [SDK](https://github.com/LedgerHQ/ledger-secure-sdk/blob/master/include/os_io.h). +### Command APDU encoding -### Sign Basic Transaction +| *Name* | *Description* | *Length* | +|--------|-----------------------------------------------------------------------|----------| +| CLA | Instruction class - indicates the type of command | 1 | +| INS | Instruction code - indicates the specific command | 1 | +| P1 | Instruction parameter 1 for the command | 1 | +| P2 | Instruction parameter 2 for the command | 1 | +| Lc | The number of bytes of command data to follow (a value from 0 to 255) | 1 | +| CData | Command data with `Lc` bytes | variable | -#### Description +### Response APDU encoding -This command signs a Nimiq basic transaction and lets users validate the operation details +| *Name* | *Description* | *Length* | +|--------|------------------------------|----------| +| RData | Response data (can be empty) | variable | +| SW | [Status word](#status-words) | 2 | -#### Coding -**Command** +## Supported APDUs -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* | -|-------|--------|--------------------|-----------------|----------|------| -| E0 | 04 |00: first apdu |00: last apdu | | | -| | |80: not first apdu |80: not last apdu| | | +### Get Public Key +#### Description -**Input data (first transaction data block)** +This command returns the public key for the given BIP 32 path. An optional message can be sent to sign to verify +the validity of the generated keypair. Optionally, the address can also be shown to the user for confirmation. -| *Description* | *Length* | -|-----------------------------------------------------------------------------------|----------| -| Number of BIP 32 derivations to perform (max 10) | 1 | -| First derivation index (big endian) | 4 | -| ... | 4 | -| Last derivation index (big endian) | 4 | -| Serialized transaction chunk size | 1 | -| Serialized transaction chunk | variable | +#### Encoding -**Input data (other transaction data block)** +**Command** -| *Description* | *Length* | -|-----------------------------------------------------------------------------------|----------| -| Serialized transaction chunk size | 1 | -| Serialized transaction chunk | variable | +| *CLA* | *INS* | *P1* | *P2* | +|-------|-------|-----------------------------|---------------------------------| +| E0 | 02 | 00 : don't return signature | 00 : don't ask for confirmation | +| | | 01 : return signature | 01 : ask for confirmation | + +**Input data** + +| *Description* | *Length* | +|---------------------------------------------------------------------|-------------------| +| Bip32 path length (max 10) | 1 | +| First Bip32 path entry (big endian) | 4 | +| ... | 4 | +| Last Bip32 path entry (big endian) | 4 | +| Message to sign, if requested. Must start with prefix "dummy-data:" | variable (max 31) | **Output data** -| *Description* | *Length* | -|-----------------------------------------------------------------------------------|----------| -| EDDSA encoded signature (ed25519) | 64 | +| *Description* | *Length* | +|-------------------------|----------| +| Public Key | 32 | +| Signature, if requested | 64 | -### Get app configuration +### Sign Transaction #### Description -This command returns specific application configuration. +This command shows the details of a transaction to the user and signs it upon confirmation. -#### Coding +#### Encoding **Command** -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* | -|-------|--------|--------------------|------------|----------|------| -| E0 | 06 | | | | | +| *CLA* | *INS* | *P1* | *P2* | +|-------|-------|--------------------|-------------------| +| E0 | 04 | 00: first apdu | 00: last apdu | +| | | 80: not first apdu | 80: not last apdu | -**Input data** +**Input data (first transaction data chunk)** + +| *Description* | *Length* | +|-------------------------------------------------|----------| +| Bip32 path length (max 10) | 1 | +| First Bip32 path entry (big endian) | 4 | +| ... | 4 | +| Last Bip32 path entry (big endian) | 4 | +| Transaction version (00: Legacy, 01: Albatross) | 1 | +| Serialized transaction chunk | variable | + +**Input data (other transaction data chunk)** + +| *Description* | *Length* | +|------------------------------|----------| +| Serialized transaction chunk | variable | + +**Serialized transaction format** -None +The combined transaction chunks form the serialized transaction to sign. They are encoded as follows: +- [Legacy transactions](https://nimiq-network.github.io/developer-reference/chapters/transactions.html#extended-transaction) +- [Albatross transactions](https://www.nimiq.com/developers/learn/protocol/transactions) +- Note that the data to sign is always in extended transaction format, regardless of whether the transaction might be + stored in reduced basic transaction format in the blockchain. **Output data** -| *Description* | *Length* | -|-----------------------------------------------------------------------------------|----------| -| Flags | 1 | -| Application major version | 1 | -| Application minor version | 1 | -| Application patch version | 1 | +| *Description* | *Length* | +|---------------------------------------------------|----------| +| EDDSA encoded transaction signature (ed25519) | 64 | +| Optional EDDSA encoded staker signature (ed25519) | 64 | -## Transport protocol +The staker signature is returned only for staking transactions for which an empty signature proof was provided in the +transaction data, instead of a pre-signed staker signature proof. In this case, the Nimiq app creates the staker +signature proof automatically, with the same key as staker as the transaction sender, instead of the user having to +create the staker signature proof separately, for this case which is the most common case. -### General transport description -Ledger APDUs requests and responses are encapsulated using a flexible protocol allowing to fragment large payloads over different underlying transport mechanisms. +### Sign Message -The common transport header is defined as follows: +#### Description -| *Description* | *Length* | -|-----------------------------------------------------------------------------------|----------| -| Communication channel ID (big endian) | 2 | -| Command tag | 1 | -| Packet sequence index (big endian) | 2 | -| Payload | var | +This command shows a message to the user and upon confirmation signs it in Nimiq format, see below. + +#### Encoding -The Communication channel ID allows commands multiplexing over the same physical link. It is not used for the time being, and should be set to 0101 to avoid compatibility issues with implementations ignoring a leading 00 byte. +**Command** -The Command tag describes the message content. Use TAG_APDU (0x05) for standard APDU payloads, or TAG_PING (0x02) for a simple link test. +| *CLA* | *INS* | *P1* | *P2* | +|-------|-------|--------------------|-------------------| +| E0 | 0A | 00: first apdu | 00: last apdu | +| | | 80: not first apdu | 80: not last apdu | -The Packet sequence index describes the current sequence for fragmented payloads. The first fragment index is 0x00. +**Input data (first message data chunk)** -### APDU Command payload encoding +| *Description* | *Length* | +|-------------------------------------------------------------------|----------| +| Bip32 path length (max 10) | 1 | +| First Bip32 path entry (big endian) | 4 | +| ... | 4 | +| Last Bip32 path entry (big endian) | 4 | +| Flags (00: none, 01: prefer hex display, 02: prefer hash display) | 1 | +| Message length (big endian) | 4 | +| Serialized message chunk | variable | -APDU Command payloads are encoded as follows: +**Input data (other message data chunk)** | *Description* | *Length* | |-----------------------------------------------------------------------------------|----------| -| APDU length (big endian) | 2 | -| APDU CLA | 1 | -| APDU INS | 1 | -| APDU P1 | 1 | -| APDU P2 | 1 | -| APDU length | 1 | -| Optional APDU data | var | +| Serialized transaction chunk | variable | -APDU payload is encoded according to the APDU case +**Serialized message format** -| Case Number | *Lc* | *Le* | Case description | -|--------------|------|------|-----------------------------------------------------------| -| 1 | 0 | 0 | No data in either direction - L is set to 00 | -| 2 | 0 | !0 | Input Data present, no Output Data - L is set to Lc | -| 3 | !0 | 0 | Output Data present, no Input Data - L is set to Le | -| 4 | !0 | !0 | Both Input and Output Data are present - L is set to Lc | +The combined message chunks form the plain binary message to sign. The message can always be displayed as message hash. +If it is at most [MAX_PRINTABLE_MESSAGE_LENGTH](https://github.com/nimiq/ledger-app-nimiq/blob/master/src/constants.h) +bytes long, it can be displayed as hex. If it additionally consists of ASCII characters only, it can be displayed as +ASCII text. -### APDU Response payload encoding +The actually signed message is in Nimiq message format, which is the sha256 hash of prefix "\x16Nimiq Signed Message:\n" +(where \x16, decimal 22, is the prefix length), of the message length rendered as decimal string, and of the plain +message. This is calculated automatically by the Nimiq app. Signing in Nimiq message format makes the calculated +signature recognisable as a Nimiq specific signature and prevents signing arbitrary data, e.g. a transaction. -APDU Response payloads are encoded as follows: +**Output data** + +| *Description* | *Length* | +|-----------------------------------|----------| +| Nimia message signature (ed25519) | 64 | -| *Description* | *Length* | -|-----------------------------------------------------------------------------------|----------| -| APDU response length (big endian) | 2 | -| APDU response data and Status Word | var | -### USB mapping +### Keep Alive -Messages are exchanged with the dongle over HID endpoints over interrupt transfers, with each chunk being 64 bytes long. The HID Report ID is ignored. +#### Description -### BLE mapping +This command can be sent to continue an async request previously interrupted by the app by a SW_KEEP_ALIVE heartbeat. +This can be used to work around timeouts of WebAuthn and U2F transport types, by renewing the request, but provides no +real value for other transport types, which do not time out and for which the app doesn't send heartbeats. -A similar encoding is used over BLE, without the Communication channel ID. +#### Encoding -The application acts as a GATT server defining service UUID D973F2E0-B19E-11E2-9E96-0800200C9A66 +**Command** -When using this service, the client sends requests to the characteristic D973F2E2-B19E-11E2-9E96-0800200C9A66, and gets notified on the characteristic D973F2E1-B19E-11E2-9E96-0800200C9A66 after registering for it. +| *CLA* | *INS* | *P1* | *P2* | +|-------|-------|--------|--------| +| E0 | 08 | unused | unused | -Requests are encoded using the standard BLE 20 bytes MTU size +**Input and output data** -## Status Words +This request has no own input and output data. It is used to extend a previous request. -The following standard Status Words are returned for all APDUs - some specific Status Words can be used for specific commands. -**Status Words** +## Status Words -| *SW* | *Description* | -|----------|--------------------------------------------------| -| 6700 | Incorrect length | -| 6982 | Security status not satisfied (Canceled by user) | -| 6A80 | Invalid data | -| 6B00 | Incorrect parameter P1 or P2 | -| 6C2x | Unsupported Transaction | -| 6Fxx | Technical problem (Internal error, please report)| -| 9000 | Normal ending of the command | +Status words tend to be similar to common +[APDU responses](https://www.eftlab.com/knowledge-base/complete-list-of-apdu-responses/) in the industry. + +| *Name* | *Description* | *SW* | +|----------------------|---------------------------------------------|------| +| SW_OK | Normal ending of the command | 9000 | +| SW_DENY | Request denied by the user | 6985 | +| SW_INCORRECT_DATA | Incorrect data | 6A80 | +| SW_NOT_SUPPORTED | Request not currently supported | 6A82 | +| SW_WRONG_P1P2 | Incorrect P1 or P2 | 6A86 | +| SW_WRONG_DATA_LENGTH | Incorrect length | 6A87 | +| SW_INS_NOT_SUPPORTED | Unexpected INS | 6D00 | +| SW_CLA_NOT_SUPPORTED | Unexpected CLA | 6E00 | +| SW_KEEP_ALIVE | Heartbeat response to avoid U2F timeouts | 6E02 | +| SW_BAD_STATE | Bad state | B007 | +| SW_CRYPTOGRAPHY_FAIL | Failure of a cryptography related operation | B008 | \ No newline at end of file diff --git a/glyphs/app_nimiq_64px.gif b/glyphs/app_nimiq_64px.gif new file mode 100644 index 0000000..23add52 Binary files /dev/null and b/glyphs/app_nimiq_64px.gif differ diff --git a/glyphs/badge_nimiq.gif b/glyphs/badge_nimiq.gif deleted file mode 100644 index 7bce335..0000000 Binary files a/glyphs/badge_nimiq.gif and /dev/null differ diff --git a/glyphs/badge_transaction.gif b/glyphs/badge_transaction.gif deleted file mode 100644 index 131325d..0000000 Binary files a/glyphs/badge_transaction.gif and /dev/null differ diff --git a/glyphs/icon_back.gif b/glyphs/icon_back.gif deleted file mode 100644 index a2a7e6d..0000000 Binary files a/glyphs/icon_back.gif and /dev/null differ diff --git a/glyphs/icon_coggle.gif b/glyphs/icon_coggle.gif deleted file mode 100644 index 01c43b2..0000000 Binary files a/glyphs/icon_coggle.gif and /dev/null differ diff --git a/glyphs/icon_dashboard.gif b/glyphs/icon_dashboard.gif index 5c30551..33d9b0a 100644 Binary files a/glyphs/icon_dashboard.gif and b/glyphs/icon_dashboard.gif differ diff --git a/glyphs/icon_dashboard_x.gif b/glyphs/icon_dashboard_x.gif deleted file mode 100644 index 33d9b0a..0000000 Binary files a/glyphs/icon_dashboard_x.gif and /dev/null differ diff --git a/glyphs/icon_down.gif b/glyphs/icon_down.gif deleted file mode 100644 index 4f4e39e..0000000 Binary files a/glyphs/icon_down.gif and /dev/null differ diff --git a/glyphs/icon_left.gif b/glyphs/icon_left.gif deleted file mode 100644 index 524226b..0000000 Binary files a/glyphs/icon_left.gif and /dev/null differ diff --git a/glyphs/icon_nimiq.gif b/glyphs/icon_nimiq.gif deleted file mode 100644 index 7bce335..0000000 Binary files a/glyphs/icon_nimiq.gif and /dev/null differ diff --git a/glyphs/icon_right.gif b/glyphs/icon_right.gif deleted file mode 100644 index 15ff3cf..0000000 Binary files a/glyphs/icon_right.gif and /dev/null differ diff --git a/glyphs/icon_stellar.gif b/glyphs/icon_stellar.gif deleted file mode 100644 index ca07ccb..0000000 Binary files a/glyphs/icon_stellar.gif and /dev/null differ diff --git a/glyphs/icon_toggle_reset.gif b/glyphs/icon_toggle_reset.gif deleted file mode 100644 index 450bc86..0000000 Binary files a/glyphs/icon_toggle_reset.gif and /dev/null differ diff --git a/glyphs/icon_toggle_set.gif b/glyphs/icon_toggle_set.gif deleted file mode 100644 index 571264c..0000000 Binary files a/glyphs/icon_toggle_set.gif and /dev/null differ diff --git a/glyphs/icon_up.gif b/glyphs/icon_up.gif deleted file mode 100644 index 4e13c06..0000000 Binary files a/glyphs/icon_up.gif and /dev/null differ diff --git a/glyphs/icon_warning.gif b/glyphs/icon_warning.gif deleted file mode 100644 index 08bd4a7..0000000 Binary files a/glyphs/icon_warning.gif and /dev/null differ diff --git a/nanox_app_nimiq.gif b/icons/app_nimiq_14px.gif similarity index 100% rename from nanox_app_nimiq.gif rename to icons/app_nimiq_14px.gif diff --git a/nanos_app_nimiq.gif b/icons/app_nimiq_16px.gif similarity index 100% rename from nanos_app_nimiq.gif rename to icons/app_nimiq_16px.gif diff --git a/icons/app_nimiq_32px.gif b/icons/app_nimiq_32px.gif new file mode 100644 index 0000000..5e9d363 Binary files /dev/null and b/icons/app_nimiq_32px.gif differ diff --git a/icons/app_nimiq_40px.gif b/icons/app_nimiq_40px.gif new file mode 100644 index 0000000..bb84c04 Binary files /dev/null and b/icons/app_nimiq_40px.gif differ diff --git a/ledger_app.toml b/ledger_app.toml index 8f4c403..30b7255 100644 --- a/ledger_app.toml +++ b/ledger_app.toml @@ -1,4 +1,10 @@ +# See https://github.com/LedgerHQ/ledgered/blob/master/doc/manifest.md for format of this file + [app] build_directory = "./" sdk = "C" -devices = ["nanos", "nanox", "nanos+"] +devices = ["nanos", "nanox", "nanos+", "stax", "flex"] + +[tests] +# unit_directory = "./unit-tests/" # not currently enabled as the tests are outdated +pytest_directory = "./tests/" diff --git a/src/base32.c b/src/base32.c index c49f0f1..21effc7 100644 --- a/src/base32.c +++ b/src/base32.c @@ -19,20 +19,22 @@ #include "base32.h" -int base32_encode(const uint8_t *data, int length, char *result, int bufSize) { +WARN_UNUSED_RESULT +error_t base32_encode(const uint8_t *data, int length, char *result, int buf_size) { int count = 0; int quantum = 8; - if (length < 0 || length > (1 << 28)) { - return -1; - } + RETURN_ON_ERROR( + length < 0 || length > (1 << 28), + ERROR_INVALID_LENGTH + ); if (length > 0) { int buffer = data[0]; int next = 1; int bitsLeft = 8; - while (count < bufSize && (bitsLeft > 0 || next < length)) { + while (count < buf_size && (bitsLeft > 0 || next < length)) { if (bitsLeft < 5) { if (next < length) { buffer <<= 8; @@ -58,7 +60,7 @@ int base32_encode(const uint8_t *data, int length, char *result, int bufSize) { // If the number of encoded characters does not make a full quantum, insert padding if (quantum != 8) { - while (quantum > 0 && count < bufSize) { + while (quantum > 0 && count < buf_size) { result[count++] = '='; quantum--; } @@ -66,10 +68,11 @@ int base32_encode(const uint8_t *data, int length, char *result, int bufSize) { } // Finally check if we exceeded buffer size. - if (count < bufSize) { - result[count] = '\000'; - return count; - } else { - return -1; - } + RETURN_ON_ERROR( + count >= buf_size, + ERROR_INVALID_LENGTH + ); + + result[count] = '\000'; + return ERROR_NONE; } \ No newline at end of file diff --git a/src/base32.h b/src/base32.h index 40a9c07..621a1ce 100644 --- a/src/base32.h +++ b/src/base32.h @@ -23,7 +23,10 @@ #include #include +#include "constants.h" +#include "error_macros.h" -int base32_encode(const uint8_t *data, int length, char *result, int bufSize); +WARN_UNUSED_RESULT +error_t base32_encode(const uint8_t *data, int length, char *result, int buf_size); #endif diff --git a/src/blake2b.c b/src/blake2b.c deleted file mode 100644 index 5698bab..0000000 --- a/src/blake2b.c +++ /dev/null @@ -1,181 +0,0 @@ -// blake2b.c -// A simple BLAKE2b Reference Implementation. - -#include "blake2b.h" - -// Cyclic right rotation. - -#ifndef ROTR64 -#define ROTR64(x, y) (((x) >> (y)) ^ ((x) << (64 - (y)))) -#endif - -// Little-endian byte access. - -#define B2B_GET64(p) \ - (((uint64_t) ((uint8_t *) (p))[0]) ^ \ - (((uint64_t) ((uint8_t *) (p))[1]) << 8) ^ \ - (((uint64_t) ((uint8_t *) (p))[2]) << 16) ^ \ - (((uint64_t) ((uint8_t *) (p))[3]) << 24) ^ \ - (((uint64_t) ((uint8_t *) (p))[4]) << 32) ^ \ - (((uint64_t) ((uint8_t *) (p))[5]) << 40) ^ \ - (((uint64_t) ((uint8_t *) (p))[6]) << 48) ^ \ - (((uint64_t) ((uint8_t *) (p))[7]) << 56)) - -// G Mixing function. - -#define B2B_G(a, b, c, d, x, y) { \ - v[a] = v[a] + v[b] + x; \ - v[d] = ROTR64(v[d] ^ v[a], 32); \ - v[c] = v[c] + v[d]; \ - v[b] = ROTR64(v[b] ^ v[c], 24); \ - v[a] = v[a] + v[b] + y; \ - v[d] = ROTR64(v[d] ^ v[a], 16); \ - v[c] = v[c] + v[d]; \ - v[b] = ROTR64(v[b] ^ v[c], 63); } - -// Initialization Vector. - -static const uint64_t blake2b_iv[8] = { - 0x6A09E667F3BCC908, 0xBB67AE8584CAA73B, - 0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1, - 0x510E527FADE682D1, 0x9B05688C2B3E6C1F, - 0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179 -}; - -// Compression function. "last" flag indicates last block. - -static void blake2b_compress(blake2b_ctx *ctx, int last) -{ - const uint8_t sigma[12][16] = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } - }; - int i; - uint64_t v[16], m[16]; - - for (i = 0; i < 8; i++) { // init work variables - v[i] = ctx->h[i]; - v[i + 8] = blake2b_iv[i]; - } - - v[12] ^= ctx->t[0]; // low 64 bits of offset - v[13] ^= ctx->t[1]; // high 64 bits - if (last) // last block flag set ? - v[14] = ~v[14]; - - for (i = 0; i < 16; i++) // get little-endian words - m[i] = B2B_GET64(&ctx->b[8 * i]); - - for (i = 0; i < 12; i++) { // twelve rounds - B2B_G( 0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]); - B2B_G( 1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]); - B2B_G( 2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]); - B2B_G( 3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]); - B2B_G( 0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]); - B2B_G( 1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]); - B2B_G( 2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]]); - B2B_G( 3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]]); - } - - for( i = 0; i < 8; ++i ) - ctx->h[i] ^= v[i] ^ v[i + 8]; -} - -// Initialize the hashing context "ctx" with optional key "key". -// 1 <= outlen <= 64 gives the digest size in bytes. -// Secret key (also <= 64 bytes) is optional (keylen = 0). - -int blake2b_init(blake2b_ctx *ctx, size_t outlen, - const void *key, size_t keylen) // (keylen=0: no key) -{ - size_t i; - - if (outlen == 0 || outlen > 64 || keylen > 64) - return -1; // illegal parameters - - for (i = 0; i < 8; i++) // state, "param block" - ctx->h[i] = blake2b_iv[i]; - ctx->h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen; - - ctx->t[0] = 0; // input count low word - ctx->t[1] = 0; // input count high word - ctx->c = 0; // pointer within buffer - ctx->outlen = outlen; - - for (i = keylen; i < 128; i++) // zero input block - ctx->b[i] = 0; - if (keylen > 0) { - blake2b_update(ctx, key, keylen); - ctx->c = 128; // at the end - } - - return 0; -} - -// Add "inlen" bytes from "in" into the hash. - -void blake2b_update(blake2b_ctx *ctx, - const void *in, size_t inlen) // data bytes -{ - size_t i; - - for (i = 0; i < inlen; i++) { - if (ctx->c == 128) { // buffer full ? - ctx->t[0] += ctx->c; // add counters - if (ctx->t[0] < ctx->c) // carry overflow ? - ctx->t[1]++; // high word - blake2b_compress(ctx, 0); // compress (not last) - ctx->c = 0; // counter to zero - } - ctx->b[ctx->c++] = ((const uint8_t *) in)[i]; - } -} - -// Generate the message digest (size given in init). -// Result placed in "out". - -void blake2b_final(blake2b_ctx *ctx, void *out) -{ - size_t i; - - ctx->t[0] += ctx->c; // mark last block offset - if (ctx->t[0] < ctx->c) // carry overflow - ctx->t[1]++; // high word - - while (ctx->c < 128) // fill up with zeros - ctx->b[ctx->c++] = 0; - blake2b_compress(ctx, 1); // final block flag = 1 - - // little endian convert and store - for (i = 0; i < ctx->outlen; i++) { - ((uint8_t *) out)[i] = - (ctx->h[i >> 3] >> (8 * (i & 7))) & 0xFF; - } -} - -// Convenience function for all-in-one computation. - -int blake2b(void *out, size_t outlen, - const void *key, size_t keylen, - const void *in, size_t inlen) -{ - blake2b_ctx ctx; - - if (blake2b_init(&ctx, outlen, key, keylen)) - return -1; - blake2b_update(&ctx, in, inlen); - blake2b_final(&ctx, out); - - return 0; -} - diff --git a/src/blake2b.h b/src/blake2b.h deleted file mode 100644 index 44cdc30..0000000 --- a/src/blake2b.h +++ /dev/null @@ -1,39 +0,0 @@ -// blake2b.h -// BLAKE2b Hashing Context and API Prototypes - -#ifndef BLAKE2B_H -#define BLAKE2B_H - -#include -#include - -// state context -typedef struct { - uint8_t b[128]; // input buffer - uint64_t h[8]; // chained state - uint64_t t[2]; // total number of bytes - size_t c; // pointer for b[] - size_t outlen; // digest size -} blake2b_ctx; - -// Initialize the hashing context "ctx" with optional key "key". -// 1 <= outlen <= 64 gives the digest size in bytes. -// Secret key (also <= 64 bytes) is optional (keylen = 0). -int blake2b_init(blake2b_ctx *ctx, size_t outlen, - const void *key, size_t keylen); // secret key - -// Add "inlen" bytes from "in" into the hash. -void blake2b_update(blake2b_ctx *ctx, // context - const void *in, size_t inlen); // data to be hashed - -// Generate the message digest (size given in init). -// Result placed in "out". -void blake2b_final(blake2b_ctx *ctx, void *out); - -// All-in-one convenience function. -int blake2b(void *out, size_t outlen, // return buffer for digest - const void *key, size_t keylen, // optional secret key - const void *in, size_t inlen); // data to be hashed - -#endif - diff --git a/src/constants.h b/src/constants.h new file mode 100644 index 0000000..1c61f21 --- /dev/null +++ b/src/constants.h @@ -0,0 +1,303 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifndef _NIMIQ_CONSTANTS_H_ +#define _NIMIQ_CONSTANTS_H_ + +#include // for uint8_t + +#include "utility_macros.h" // for MAX and STRING_LENGTH_WITH_SUFFIX + +// The maximum theoretically allowed NIM amount. Equal to JavaScript's Number.MAX_SAFE_INTEGER, see Coin::MAX_SAFE_VALUE +// in primitives/src/coin.rs in core-rs-albatross. Notably, this theoretical value is higher than the planned final +// total supply. +#define MAX_SAFE_LUNA_AMOUNT 9007199254740991 + +#define STRING_LENGTH_WITH_SUFFIX(length_a, suffix) (length_a - /* string terminator of string a */ 1 + sizeof(suffix)) + +// String lengths including the string terminator. +// Note that the measurement strings here don't end up in the app binary, only the measurement results of sizeof. +#define STRING_LENGTH_YES_NO MAX(sizeof("Yes"), sizeof("No")) +#define STRING_LENGTH_UINT8 sizeof("255") // "0" to "255" (any uint8) +#define STRING_LENGTH_UINT32 sizeof("4294967295") // "0" to "4294967295" (any uint32) +#define STRING_LENGTH_NIM_AMOUNT sizeof("90071992547.40991") // "0" to "90071992547.40991", MAX_SAFE_LUNA_AMOUNT in NIM +#define STRING_LENGTH_NIM_AMOUNT_WITH_TICKER STRING_LENGTH_WITH_SUFFIX(STRING_LENGTH_NIM_AMOUNT, " NIM") +#define STRING_LENGTH_USER_FRIENDLY_ADDRESS sizeof("NQ07 0000 0000 0000 0000 0000 0000 0000 0000") + +#define MAX_BIP32_PATH_LENGTH 10 + +#define CASHLINK_MAGIC_NUMBER "\x00\x82\x80\x92\x87" +#define CASHLINK_MAGIC_NUMBER_LENGTH 5 + +#define MESSAGE_SIGNING_PREFIX "\x16Nimiq Signed Message:\n" // 0x16 (decimal 22) is the prefix length + +#define TX_FLAG_CONTRACT_CREATION (0x1 << 0) +#define TX_FLAG_SIGNALING (0x1 << 1) +#define MESSAGE_FLAG_PREFER_DISPLAY_TYPE_HEX (0x1 << 0) +#define MESSAGE_FLAG_PREFER_DISPLAY_TYPE_HASH (0x1 << 1) + +// Max supported length for a transaction's serialized content, based on Albatross, where data is generally longer due +// to added sender data and uint64 timestamps instead of uint32 block counts in vesting and htlc contracts. Sum of: +// - Minimum of 67 bytes common to all transactions for recipient data length, sender address, sender type, recipient +// address, recipient type, value, fee, validity start height, network id, flags, sender data length (assuming sender +// data length being encoded in a single byte varint, i.e. sender data up to 127 bytes length), recipient data length. +// - Sender or recipient data of up to 121 bytes. Note that currently, we don't support sender data and recipient data +// to be set at the same time. This number is the maximum of: +// 1 byte sender data for OutgoingStakingTransactionData. +// 64 bytes recipient data limit we set for basic transactions. +// 52 bytes recipient data for fully specified VestingContract CreationTransactionData. +// 114 bytes recipient data for HashedTimeLockedContract CreationTransactionData with Sha512 hash root. +// Staking recipient data for IncomingStakingTransactionData (including enum index), which can be: +// 120 bytes for fully specified CreateStaker with ed25519 signature proof with empty merkle path. +// 21 bytes for AddStake. +// 121 bytes for fully specified UpdateStaker with ed25519 signature proof with empty merkle path. +// 107 bytes for SetActiveStake. +// 107 bytes for RetireStake. +// (Validator transactions are not covered yet, as not supported yet.) +#define MAX_RAW_TX 188 +// Limit printable message length as Nano S has only about 4kB of RAM total, used for global vars and stack, and on top +// of the message buffer, there is the buffer for the printed message, which is twice as large, see messageSigningContext_t +// in globals.h Additionally, the paging ui displays only ~16 chars per page on Nano S. +#define MAX_PRINTABLE_MESSAGE_LENGTH 160 // 10+ pages ascii or 20 pages hex on Nano S + +typedef enum { + TRANSACTION_VERSION_LEGACY, + TRANSACTION_VERSION_ALBATROSS, +} transaction_version_t; + +typedef enum { + ACCOUNT_TYPE_BASIC = 0, + ACCOUNT_TYPE_VESTING = 1, + ACCOUNT_TYPE_HTLC = 2, + ACCOUNT_TYPE_STAKING = 3, +} account_type_t; + +typedef enum { + HASH_ALGORITHM_BLAKE2B = 1, + HASH_ALGORITHM_ARGON2D = 2, + HASH_ALGORITHM_SHA256 = 3, + HASH_ALGORITHM_SHA512 = 4, +} hash_algorithm_t; + +typedef enum { + TRANSACTION_TYPE_NORMAL, + TRANSACTION_TYPE_VESTING_CREATION, + TRANSACTION_TYPE_HTLC_CREATION, + TRANSACTION_TYPE_STAKING_INCOMING, + TRANSACTION_TYPE_STAKING_OUTGOING, +} transaction_type_t; + +typedef enum { + TRANSACTION_LABEL_TYPE_REGULAR_TRANSACTION, + TRANSACTION_LABEL_TYPE_CASHLINK, + TRANSACTION_LABEL_TYPE_VESTING_CREATION, + TRANSACTION_LABEL_TYPE_HTLC_CREATION, + TRANSACTION_LABEL_TYPE_STAKING_CREATE_STAKER, + TRANSACTION_LABEL_TYPE_STAKING_ADD_STAKE, + TRANSACTION_LABEL_TYPE_STAKING_UPDATE_STAKER, + TRANSACTION_LABEL_TYPE_STAKING_SET_ACTIVE_STAKE, + TRANSACTION_LABEL_TYPE_STAKING_RETIRE_STAKE, + TRANSACTION_LABEL_TYPE_STAKING_REMOVE_STAKE, +} transaction_label_type_t; + +// Recipient data type for incoming transactions to the staking contract. +typedef enum { + CREATE_VALIDATOR, + UPDATE_VALIDATOR, + DEACTIVATE_VALIDATOR, + REACTIVATE_VALIDATOR, + RETIRE_VALIDATOR, + CREATE_STAKER, + ADD_STAKE, + UPDATE_STAKER, + SET_ACTIVE_STAKE, + RETIRE_STAKE, +} staking_incoming_data_type_t; +// Sender data type for outgoing transactions from the staking contract. +typedef enum { + DELETE_VALIDATOR, + REMOVE_STAKE, +} staking_outgoing_data_type_t; + +typedef enum { + MESSAGE_DISPLAY_TYPE_ASCII, + MESSAGE_DISPLAY_TYPE_HEX, + MESSAGE_DISPLAY_TYPE_HASH, +} message_display_type_t; + +/** + * Error codes for methods that do not operate on the APDU protocol. They are meant to keep these methods somewhat + * independent of Ledger specific code. + */ +typedef enum: uint8_t { + /** + * No error occurred. All good. + */ + ERROR_NONE = 0x0, + /** + * Not further specified error of value 1/true, which is the automatic result of logical expressions, or variables + * and return types of type bool that evaluate to true. This error is mostly defined to correctly classify such + * automatic results. + * + * Instead of using this error, one of the more specific other errors should be used. + */ + ERROR_TRUE = 0x1, + /** + * An unexpected error occurred that shouldn't have happened. + * + * Instead of using this error, preferably exit the app immediately, for example via a LEDGER_ASSERT, for less + * overhead of error handling in functions that otherwise don't error. + */ + ERROR_UNEXPECTED = 0x2, + /** + * Error reading data from a buffer, typically because the buffer is shorter than expected, but could also be + * because of unexpected values being read, for example when reading structured data like signature proofs. + */ + ERROR_READ = 0x3, + /** + * The length of some data is longer than allowed, or does not match the expected or advertised length. + */ + ERROR_INVALID_LENGTH = 0x4, + /** + * The data is semantically incorrect. It might for example contain a disallowed, inconsistent or unexpected value. + */ + ERROR_INCORRECT_DATA = 0x5, + /** + * Some operation or data is not supported by the ledger app or core-albatross. + */ + ERROR_NOT_SUPPORTED = 0x6, + /** + * An error in a call of a cryptography method occurred, typically because of incorrect parameters passed, but also + * for example if a method aborted because the device is locked. + */ + ERROR_CRYPTOGRAPHY = 0x7, +} error_t; +#define ERROR_TRUE _Pragma("GCC warning \"Use of ERROR_TRUE is discouraged\"") ERROR_TRUE +#define ERROR_UNEXPECTED _Pragma("GCC warning \"Use of ERROR_UNEXPECTED is discouraged\"") ERROR_UNEXPECTED + +/** + * APDU status words for methods that operate on the APDU protocol. + * The ones we're using are mostly based on app-bitcoin-new's sw.h, which itself is based on app-boilerplate's sw.h with + * some additions from old Bitcoin app's btchip_apdu_constants, and should mostly also be supported by @ledgerhq/errors. + * Note however, that the error codes for some of the errors have changed from btchip_apdu_constants, for example for + * INCORRECT_P1_P2. + * They should be generally following smart card APDU standard ISO-7816. If we were to define custom errors, they should + * be in the range 0xB000-0xEFFF, see errors.h in ledger-secure-sdk. + * Definition of other common status words: + * - https://de.wikipedia.org/wiki/Application_Protocol_Data_Unit#Statusw%C3%B6rter + * - https://github.com/LedgerHQ/ledger-secure-sdk/blob/master/include/errors.h + * - https://github.com/LedgerHQ/app-bitcoin-new/blob/master/src/boilerplate/sw.h + * - https://github.com/LedgerHQ/app-boilerplate/blob/master/src/sw.h + * - https://github.com/LedgerHQ/ledger-live/blob/develop/libs/ledgerjs/packages/errors/src/index.ts + * - https://github.com/LedgerHQ/app-bitcoin/blob/master/include/btchip_apdu_constants.h + * - https://ledgerhq.github.io/btchip-doc/bitcoin-technical-beta.html#_status_words + */ +typedef enum: uint16_t { + /** + * Status word for success. + */ + SW_OK = 0x9000, + /** + * Status word for denied by user. + */ + SW_DENY = 0x6985, + /** + * Status word for incorrect data. + */ + SW_INCORRECT_DATA = 0x6A80, + /** + * Status word for request not currently supported. + */ + SW_NOT_SUPPORTED = 0x6A82, + /** + * Status word for incorrect P1 or P2. + */ + SW_WRONG_P1P2 = 0x6A86, + /** + * Status word for either wrong Lc or length of APDU command less than 5. + */ + SW_WRONG_DATA_LENGTH = 0x6A87, + /** + * Status word for unknown command with this INS. + */ + SW_INS_NOT_SUPPORTED = 0x6D00, + /** + * Status word for instruction class is different than CLA. + */ + SW_CLA_NOT_SUPPORTED = 0x6E00, + /** + * Status word for a heartbeat response to avoid U2F timeouts. The client is supposed to reply with instruction + * INS_KEEP_ALIVE, to continue the request. + */ + SW_KEEP_ALIVE = 0x6E02, + /** + * Status word for bad state. + */ + SW_BAD_STATE = 0xB007, + /** + * Status word for failure of a cryptography related operation. + * In app-bitcoin-new and app-boilerplate, this is called SW_SIGNATURE_FAIL, but we use it in a more general way not + * only for failed signatures but all cryptographic operations like public key derivation. + */ + SW_CRYPTOGRAPHY_FAIL = 0xB008, + + // Additional status words defined in app-bitcoin-new's sw.h, which we don't currently use: + // /** + // * Status word for command not valid for security reasons (for example: device needs to be unlocked with PIN). + // */ + // SW_SECURITY_STATUS_NOT_SATISFIED = 0x6982, + // /** + // * Status word for fail in Swap + // */ + // SW_FAIL_SWAP = 0x6B00, + // /** + // * Status word for wrong response length (buffer too small or too big). + // */ + // SW_WRONG_RESPONSE_LENGTH = 0xB000, + // /** + // * Status word for interrupted execution. + // */ + // SW_INTERRUPTED_EXECUTION = 0xE000, + + // Additional custom status words defined in app-boilerplate's sw.h, which we don't currently use: + // /** + // * Status word for fail to display BIP32 path. + // */ + // SW_DISPLAY_BIP32_PATH_FAIL = 0xB001, + // /** + // * Status word for fail to display address. + // */ + // SW_DISPLAY_ADDRESS_FAIL = 0xB002, + // /** + // * Status word for fail to display amount. + // */ + // SW_DISPLAY_AMOUNT_FAIL = 0xB003, + // /** + // * Status word for wrong transaction length. + // */ + // SW_WRONG_TX_LENGTH = 0xB004, + // /** + // * Status word for fail of transaction parsing. + // */ + // SW_TX_PARSING_FAIL = 0xB005, + // /** + // * Status word for fail of transaction hash. + // */ + // SW_TX_HASH_FAIL = 0xB006, +} sw_t; + +#endif // _NIMIQ_CONSTANTS_H_ diff --git a/src/error_macros.c b/src/error_macros.c new file mode 100644 index 0000000..5e45f1e --- /dev/null +++ b/src/error_macros.c @@ -0,0 +1,42 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#include "error_macros.h" + +sw_t error_to_sw(error_t error) { + switch (error) { + case ERROR_READ: // Assume that the read error is due to too short data, which is the most common reason. + case ERROR_INVALID_LENGTH: + return SW_WRONG_DATA_LENGTH; + case ERROR_INCORRECT_DATA: + return SW_INCORRECT_DATA; + case ERROR_NOT_SUPPORTED: + return SW_NOT_SUPPORTED; + case ERROR_CRYPTOGRAPHY: + return SW_CRYPTOGRAPHY_FAIL; + case ERROR_NONE: + return SW_OK; + default: + // ERROR_TRUE, ERROR_UNEXPECTED or an error which is incorrectly missing here. Exit the app. + LEDGER_ASSERT( + false, + "Unexpected error 0x%02x", + error + ); + return SW_BAD_STATE; + } +} diff --git a/src/error_macros.h b/src/error_macros.h new file mode 100644 index 0000000..2d515a2 --- /dev/null +++ b/src/error_macros.h @@ -0,0 +1,178 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifndef _NIMIQ_ERROR_MACROS_H_ +#define _NIMIQ_ERROR_MACROS_H_ + +#include "ledger_assert.h" // For LEDGER_ASSERT in ON_ERROR, ERROR_TO_SW, and files that include error_macros.h + +#include "constants.h" // For error_t and sw_t +#include "utility_macros.h" // For VA_ARGS_* and DEBUG_EMIT macros + +#if defined(NIMIQ_DEBUG) && NIMIQ_DEBUG +#include // for strstr used in debug expression sanity check in ON_ERROR +#endif + +/** + * Return from the current function with an error code and print debug messages. + * + * error: the error code to return. Expressions of type void are not allowed. + * message: an error message to print in debug builds. Note that in contrast to the conditional ON_ERROR and its + * derivative macros like RETURN_ON_ERROR, it is mandatory here, under the assumption that the non-conditional + * error is returned where the error originates, while ON_ERROR and its derivative macros are oftentimes used for + * forwarding an error. + * Optional parameters: optional additional parameters of the message to pass to PRINTF. + */ +#define RETURN_ERROR(error, message, ...) \ + do { \ + PRINTF(" !!! Error 0x%02x returned in %s (file %s, line %d)\n", error, __func__, __FILE__, __LINE__); \ + PRINTF(" "); \ + PRINTF(message __VA_OPT__(,) __VA_ARGS__); \ + return (error); \ + } while(0) + +/** + * Conditionally run specified statements and print debug messages, if an expression results in an error. + * + * expression: an expression to evaluate. Non-zero return codes are considered errors. + * statements: statements to run on error. The received error is available as variable "received_error", which is + * independent of any custom error code passed. On the other hand, variable "error" is either the received error, or + * a custom error code if passed. The type of error depends on the type of the passed custom error. + * First optional parameter: a custom error code to assign to variable "error" can be passed. Expressions of type void + * are not allowed. To automatically convert an error of type error_t to a status word of type sw_t, ERROR_TO_SW() + * can be used. + * Second and following optional parameters: a debug message and parameters to pass to PRINTF. + * + * Note: while this macro looks like it's injecting a lot of code (which would bloat the app binary), all the PRINTFs + * and the sanity check are not included in production builds. Additionally, the compiler might be able to optimize + * variables received_error and error away (both are const, and if a custom error is passed, both are set and read only + * once (depending on statements), and if no custom error is passed, error will always get the same value as + * received_error). + */ +#define ON_ERROR(expression, statements, ...) \ + do { \ + /* In debug builds, sanity check expressions for improper use of logical expressions, where their result, */ \ + /* which is 0 or 1, is assigned and used as received error, instead of the actually received error which */ \ + /* gets lost in the logical expression. */ \ + DEBUG_EMIT({ \ + bool is_received_error_used = ( \ + /* If no custom error is set, received_error is used in any case as it's assigned to error. */ \ + VA_ARGS_IF_EMPTY_EMIT( \ + true, \ + VA_ARGS_PICK_FIRST(__VA_ARGS__) \ + ) \ + /* If a custom error is set, check whether it or the statements use received_error, which might */ \ + /* also be via ERROR_TO_SW(). */ \ + VA_ARGS_IF_NOT_EMPTY_EMIT( \ + strstr(#__VA_ARGS__, "received_error") || strstr(#__VA_ARGS__, "ERROR_TO_SW()") \ + || strstr(#statements, "received_error") || strstr(#statements, "ERROR_TO_SW()"), \ + VA_ARGS_PICK_FIRST(__VA_ARGS__) \ + ) \ + ); \ + /* Assert that the received error is either not used, or not the result of a logical expression. */ \ + LEDGER_ASSERT( \ + !is_received_error_used || !strstr(#expression, "||"), \ + "Using result of logical expression %s as error code", \ + #expression \ + ); \ + }) \ + const error_t received_error = (expression); \ + if (received_error) { \ + PRINTF(" !!! Error 0x%02x returned by %s in %s (file %s, line %d)\n", received_error, #expression, \ + __func__, __FILE__, __LINE__); \ + /* Print custom debug message if passed as second and following optional arguments. */ \ + VA_ARGS_IF_NOT_EMPTY_EMIT( \ + { PRINTF(" "); PRINTF(VA_ARGS_OMIT_FIRST(__VA_ARGS__)); }, \ + VA_ARGS_OMIT_FIRST(__VA_ARGS__) \ + ) \ + /* Assign received_error or, if passed, custom error to error. */ \ + /* No curly braces around the assignment, to avoid it being scoped to a separate scope. */ \ + /* Silence warning regarding unused variable, as the statements are not required to use variable error, */ \ + /* which should then be optimized away by the compiler. */ \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wunused-variable\"") \ + VA_ARGS_IF_EMPTY_EMIT( \ + const error_t error = received_error;, \ + VA_ARGS_PICK_FIRST(__VA_ARGS__) \ + ) \ + VA_ARGS_IF_NOT_EMPTY_EMIT( \ + const typeof(VA_ARGS_PICK_FIRST(__VA_ARGS__)) error = (VA_ARGS_PICK_FIRST(__VA_ARGS__));, \ + VA_ARGS_PICK_FIRST(__VA_ARGS__) \ + ) \ + _Pragma("GCC diagnostic pop") \ + /* Run specified statements. */ \ + { statements } \ + } \ + } while(0) + +/** + * Conditionally return from the current function, if an expression results in an error. + * + * See ON_ERROR for parameter descriptions. + */ +#define RETURN_ON_ERROR(expression, ...) \ + ON_ERROR(expression, { return error; }, __VA_ARGS__) + +/** + * Conditionally jump to a goto label, if an expression results in an error. + * + * expression: as in ON_ERROR + * goto_label: the goto label to jump to. + * First optional parameter: a variable name to assign the error to. + * Second, third and following optional parameters: are forwarded to ON_ERROR. See there for parameter descriptions. + */ +#define GOTO_ON_ERROR(expression, goto_label, ...) \ + ON_ERROR( \ + expression, \ + { \ + /* Assign error to specified variable name, if such was specified. */ \ + VA_ARGS_IF_NOT_EMPTY_EMIT( \ + { VA_ARGS_PICK_FIRST(__VA_ARGS__) = error; }, \ + VA_ARGS_PICK_FIRST(__VA_ARGS__) \ + ) \ + goto goto_label; \ + }, \ + VA_ARGS_OMIT_FIRST(__VA_ARGS__) \ + ) + +/** + * For usage as custom error in ON_ERROR and derivative macros to automatically convert an error of type error_t to a + * status word of type sw_t. + */ +sw_t error_to_sw(error_t error); +#define ERROR_TO_SW() error_to_sw(received_error) + +// Decorators + +#define UNUSED_PARAMETER(parameter) __attribute__((unused)) parameter + +// FALL_THROUGH and WARN_UNUSED_RESULT are copied over from ledger-secure-sdk's decorators.h, such that they're known to +// the IDE, even if the ledger-secure-sdk source is not available to it. + +#ifndef FALL_THROUGH +#if defined(__GNUC__) && __GNUC__ >= 7 || defined(__clang__) && __clang_major__ >= 12 +#define FALL_THROUGH __attribute__((fallthrough)) +#else +#define FALL_THROUGH ((void) 0) +#endif +#endif + +#ifndef WARN_UNUSED_RESULT +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#endif + +#endif // _NIMIQ_ERROR_MACROS_H_ diff --git a/src/globals.c b/src/globals.c new file mode 100644 index 0000000..5d35fc3 --- /dev/null +++ b/src/globals.c @@ -0,0 +1,33 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +// From Ledger SDK +#include "os_io_seproxyhal.h" +#include "ux.h" + +#include "globals.h" + +// Specified in os_io_seproxyhal.h +unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; + +// Specified in ux_bagl.h / ux_nbgl.h, included via ux.h +ux_state_t G_ux; +bolos_ux_params_t G_ux_params; + +// Specified in globals.h +const internal_storage_t N_storage_real; // const variable in flash storage; writable via nvm_write +generalContext_t ctx; diff --git a/src/globals.h b/src/globals.h new file mode 100644 index 0000000..6ba3422 --- /dev/null +++ b/src/globals.h @@ -0,0 +1,108 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifndef _NIMIQ_GLOBALS_H_ +#define _NIMIQ_GLOBALS_H_ + +#include +#include + +// From Ledger SDK +#include "cx.h" + +#include "constants.h" +#include "utility_macros.h" +#include "nimiq_utils.h" + +/** + * Global structure for NVM data storage. + */ +typedef struct internal_storage_t { + uint8_t fidoTransport; // for u2f; currently unused; enabled by default + uint8_t initialized; +} internal_storage_t; + +// extern variable, shared across .c files. Declared in globals.c +extern const internal_storage_t N_storage_real; +#define N_storage (*(volatile internal_storage_t *) PIC(&N_storage_real)) + +typedef struct publicKeyContext_t { + cx_ecfp_256_public_key_t publicKey; + char address[STRING_LENGTH_USER_FRIENDLY_ADDRESS]; + uint8_t signature[64]; + bool returnSignature; +} publicKeyContext_t; + +typedef struct transactionContext_t { + uint8_t bip32PathLength; + uint32_t bip32Path[MAX_BIP32_PATH_LENGTH]; + transaction_version_t transactionVersion; + uint8_t rawTx[MAX_RAW_TX]; + uint32_t rawTxLength; + parsed_tx_t parsed; +} transactionContext_t; + +// Printed message buffer length dimension chosen such that it can hold the printed uint32 message length +// (STRING_LENGTH_UINT32 (11) bytes), the message printed as hash (32 byte hash as hex + string terminator = 65 bytes), +// ascii (1 char per byte + string terminator) or hex (2 char per byte + string terminator). +#define PRINTED_MESSAGE_BUFFER_LENGTH (MAX_PRINTABLE_MESSAGE_LENGTH * 2 + 1) +typedef struct messageSigningContext_t { + uint32_t bip32Path[MAX_BIP32_PATH_LENGTH]; + // nimiq supports signing data of arbitrary length, but for now we restrict the length in the ledger app to uint32_t + uint32_t messageLength; + uint32_t processedMessageLength; + union { + // the memory is specifically aligned such that messageHashContext and prefixedMessageHashContext do not overlap + // with messageHash and prefxedMessageHash, see _Static_assert in handleSignMessage. + struct { + cx_sha256_t messageHashContext; + cx_sha256_t prefixedMessageHashContext; + } prepare; + struct { + message_display_type_t displayType; + char printedMessageLabel[MAX(sizeof("Message"), MAX(sizeof("Message Hex"), sizeof("Message Hash")))]; + char printedMessage[PRINTED_MESSAGE_BUFFER_LENGTH]; + uint8_t messageHash[32]; + uint8_t prefixedMessageHash[32]; + } confirm; + }; + uint8_t printableMessage[MAX_PRINTABLE_MESSAGE_LENGTH]; + bool isPrintableAscii; + uint8_t bip32PathLength; + uint8_t flags; +} messageSigningContext_t; + +typedef struct { + union { + publicKeyContext_t pk; + transactionContext_t tx; + messageSigningContext_t msg; + } req; + uint16_t u2fTimer; +} generalContext_t; + +// extern variable, shared across .c files. Declared in globals.c +extern generalContext_t ctx; + +// Shortcuts for parsed transaction data +#define PARSED_TX (ctx.req.tx.parsed) +#define PARSED_TX_NORMAL_OR_STAKING_OUTGOING (PARSED_TX.type_specific.normal_or_staking_outgoing_tx) +#define PARSED_TX_VESTING_CREATION (PARSED_TX.type_specific.vesting_creation_tx) +#define PARSED_TX_HTLC_CREATION (PARSED_TX.type_specific.htlc_creation_tx) +#define PARSED_TX_STAKING_INCOMING (PARSED_TX.type_specific.staking_incoming_tx) + +#endif // _NIMIQ_GLOBALS_H_ diff --git a/src/main.c b/src/main.c index f783d49..7fc8715 100644 --- a/src/main.c +++ b/src/main.c @@ -15,29 +15,31 @@ * limitations under the License. ********************************************************************************/ -#include "os.h" -#include "cx.h" #include -#include +#include -#include "os_io_seproxyhal.h" -#include "string.h" - -#include "ux.h" +// From Ledger SDK +#include "os.h" // for os_* methods +#include "os_io_seproxyhal.h" // for SEPROXYHAL_TAG_* and G_io_app definitions +#include "cx.h" // for cx_* types and methods +#include "ux.h" // for UX_*_EVENT +#include "constants.h" +#include "globals.h" +#include "error_macros.h" #include "nimiq_utils.h" - -unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; - -uint32_t set_result_get_publicKey(void); - -#define MAX_BIP32_PATH 10 +#include "nimiq_ux.h" #define CLA 0xE0 +// Defined instructions. +// Note that some values are disallowed as instructions (odd numbers, 6X, 9X) or predefined by ISO7816-3, see +// https://cardwerk.com/smart-card-standard-iso7816-4-section-5-basic-organizations/#:~:text=Table%2010%20%E2%80%93-,Invalid%20INS%20codes,-b8%20b7%20b6 +// or https://de.wikipedia.org/wiki/Application_Protocol_Data_Unit#command_APDU #define INS_GET_PUBLIC_KEY 0x02 #define INS_SIGN_TX 0x04 -#define INS_GET_APP_CONFIGURATION 0x06 +// #define INS_GET_APP_CONFIGURATION 0x06 // was removed, but still listing it here to avoid assigning the same value #define INS_KEEP_ALIVE 0x08 +#define INS_SIGN_MESSAGE 0x0A #define P1_NO_SIGNATURE 0x00 #define P1_SIGNATURE 0x01 #define P2_NO_CONFIRM 0x00 @@ -54,2067 +56,403 @@ uint32_t set_result_get_publicKey(void); #define OFFSET_LC 4 #define OFFSET_CDATA 5 -#define MAX_UI_STEPS 11 - -#define MAX_RAW_TX 130 - -typedef struct publicKeyContext_t { - cx_ecfp_public_key_t publicKey; - char address[45]; - uint8_t signature[64]; - bool returnSignature; -} publicKeyContext_t; - -typedef struct transactionContext_t { - uint8_t bip32PathLength; - uint32_t bip32Path[MAX_BIP32_PATH]; - uint8_t rawTx[MAX_RAW_TX]; - uint32_t rawTxLength; - txContent_t content; -} transactionContext_t; - -typedef struct { - union { - publicKeyContext_t pk; - transactionContext_t tx; - } req; - uint16_t u2fTimer; -} generalContext_t; - -generalContext_t ctx; - -volatile char operationCaption[15]; - - -#if defined(TARGET_BLUE) -volatile char displayString[33]; -volatile char subtitleCaption[16]; -bagl_element_t tmp_element; -#else -volatile char details1Caption[18]; -volatile char details2Caption[18]; -volatile char details3Caption[18]; -volatile char details4Caption[18]; -#endif - -unsigned int io_seproxyhal_touch_settings(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_exit(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_tx_ok(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_tx_cancel(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_address_ok(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_address_cancel(const bagl_element_t *e); -void ui_idle(void); - -#if defined(TARGET_NANOX) || defined(TARGET_NANOS2) -#include "ux.h" -ux_state_t G_ux; -bolos_ux_params_t G_ux_params; -#else // TARGET_NANOX -ux_state_t ux; -#endif // TARGET_NANOX - -// display stepped screens -unsigned int ux_step; -unsigned int ux_step_count; - -typedef struct internalStorage_t { - uint8_t fidoTransport; - uint8_t initialized; -} internalStorage_t; - -internalStorage_t const N_storage_real; -#define N_storage (*(internalStorage_t *)PIC(&N_storage_real)) - -const bagl_element_t *ui_menu_item_out_over(const bagl_element_t *e) { - // the selection rectangle is after the none|touchable - e = (const bagl_element_t *)(((unsigned int)e) + sizeof(bagl_element_t)); - return e; -} - -#if defined(TARGET_BLUE) - -#define BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH 10 -#define BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH 8 -#define MAX_CHAR_PER_LINE 30 - -#define COLOR_BG_1 0xF9F9F9 -#define COLOR_APP 0xF6AE2D -#define COLOR_APP_LIGHT 0x93D1ED - -const bagl_element_t ui_idle_blue[] = { - // type userid x y w h str rad - // fill fg bg fid iid txt touchparams... ] - {{BAGL_RECTANGLE, 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // erase screen (only under the status bar) - {{BAGL_RECTANGLE, 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE, 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, - BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "NIMIQ", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 56, 44, 0, 0, - BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, - BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_CENTER | - BAGL_FONT_ALIGNMENT_MIDDLE, - 0}, - BAGL_FONT_SYMBOLS_0_SETTINGS, - 0, - COLOR_APP, - 0xFFFFFF, - io_seproxyhal_touch_settings, - NULL, - NULL}, - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, - BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, - BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_CENTER | - BAGL_FONT_ALIGNMENT_MIDDLE, - 0}, - BAGL_FONT_SYMBOLS_0_DASHBOARD, - 0, - COLOR_APP, - 0xFFFFFF, - io_seproxyhal_touch_exit, - NULL, - NULL}, - - // badge_nimiq.gif - {{BAGL_ICON, 0x00, 135, 178, 50, 50, 0, 0, BAGL_FILL, 0, COLOR_BG_1, 0, 0}, - &C_badge_nimiq, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x00, 0, 270, 320, 30, 0, 0, BAGL_FILL, 0x000000, - COLOR_BG_1, - BAGL_FONT_OPEN_SANS_LIGHT_16_22PX | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Open Nimiq wallet", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x00, 0, 308, 320, 30, 0, 0, BAGL_FILL, 0x000000, - COLOR_BG_1, - BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Connect the Ledger Blue and open your", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x00, 0, 331, 320, 30, 0, 0, BAGL_FILL, 0x000000, - COLOR_BG_1, - BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "preferred wallet to view your accounts.", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x00, 0, 450, 320, 14, 0, 0, 0, 0x999999, COLOR_BG_1, - BAGL_FONT_OPEN_SANS_REGULAR_8_11PX | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Approval requests will show automatically.", - 10, - 0, - COLOR_BG_1, - NULL, - NULL, - NULL}, -}; - -unsigned int ui_idle_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { - return 0; -} -#endif // #if TARGET_BLUE - -#if defined(TARGET_NANOS) - -const ux_menu_entry_t menu_main[]; - -const ux_menu_entry_t menu_about[] = { - {NULL, NULL, 0, NULL, "Version", APPVERSION, 0, 0}, - {menu_main, NULL, 2, &C_icon_back, "Back", NULL, 61, 40}, - UX_MENU_END - }; - -const ux_menu_entry_t menu_main[] = { - {NULL, NULL, 0, &C_icon_nimiq, "Use wallet to", "view accounts", 33, 12}, - {menu_about, NULL, 0, NULL, "About", NULL, 0, 0}, - {NULL, os_sched_exit, 0, &C_icon_dashboard, "Quit app", NULL, 50, 29}, - UX_MENU_END - }; - -#endif // #if TARGET_NANOS - -#if defined(TARGET_BLUE) - -const bagl_element_t *ui_settings_blue_toggle_browser(const bagl_element_t *e) { - // toggle setting and request redraw of settings elements - uint8_t setting = N_storage.fidoTransport ? 0 : 1; - nvm_write(&N_storage.fidoTransport, (void *)&setting, sizeof(uint8_t)); - - // only refresh settings mutable drawn elements - UX_REDISPLAY_IDX(8); - - // won't redisplay the bagl_none - return 0; -} - -// don't perform any draw/color change upon finger event over settings -const bagl_element_t *ui_settings_out_over(const bagl_element_t *e) { - return NULL; -} - -unsigned int ui_settings_back_callback(const bagl_element_t *e) { - ui_idle(); - return 0; -} - -const bagl_element_t ui_settings_blue[] = { - // type userid x y w h str rad - // fill fg bg fid iid txt touchparams... ] - {{BAGL_RECTANGLE, 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // erase screen (only under the status bar) - {{BAGL_RECTANGLE, 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE, 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "SETTINGS", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 50, 44, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, - BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_CENTER | BAGL_FONT_ALIGNMENT_MIDDLE, 0}, - BAGL_FONT_SYMBOLS_0_LEFT, - 0, - COLOR_APP, - 0xFFFFFF, - ui_settings_back_callback, - NULL, - NULL}, - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, - BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, - BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_CENTER | - BAGL_FONT_ALIGNMENT_MIDDLE, - 0}, - BAGL_FONT_SYMBOLS_0_DASHBOARD, - 0, - COLOR_APP, - 0xFFFFFF, - io_seproxyhal_touch_exit, - NULL, - NULL}, - -}; - -const bagl_element_t *ui_settings_blue_prepro(const bagl_element_t *e) { - // none elements are skipped - if ((e->component.type & (~BAGL_FLAG_TOUCHABLE)) == BAGL_NONE) { - return 0; - } - // swap icon buffer to be displayed depending on if corresponding setting is - // enabled or not. - if (e->component.userid) { - os_memmove(&tmp_element, e, sizeof(bagl_element_t)); - switch (e->component.userid) { - case 0x01: - // swap icon content - if (N_storage.fidoTransport) { - tmp_element.text = &C_icon_toggle_set; - } else { - tmp_element.text = &C_icon_toggle_reset; - } - break; - } - return &tmp_element; - } - return 1; -} - -unsigned int ui_settings_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { - return 0; -} - -#endif // #if defined(TARGET_BLUE) - -#if defined(TARGET_BLUE) -const bagl_element_t ui_address_blue[] = { - {{BAGL_RECTANGLE, 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, - 0x000000, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // erase screen (only under the status bar) - {{BAGL_RECTANGLE, 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, - COLOR_APP, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE, 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, - BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "CONFIRM ADDRESS", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - //{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0, - // BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, - // BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE, - // 0 }, " " /*BAGL_FONT_SYMBOLS_0_DASHBOARD*/, 0, COLOR_APP, 0xFFFFFF, - // io_seproxyhal_touch_exit, NULL, NULL}, - - {{BAGL_LABELINE, 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999, - COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, - "ADDRESS", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x10, 30, 136, 260, 30, 0, 0, BAGL_FILL, 0x000000, - COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0}, - displayString, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x11, 30, 159, 260, 30, 0, 0, BAGL_FILL, 0x000000, - COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0}, - displayString, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0, 18, - BAGL_FILL, 0xCCCCCC, COLOR_BG_1, - BAGL_FONT_OPEN_SANS_REGULAR_11_14PX | BAGL_FONT_ALIGNMENT_CENTER | - BAGL_FONT_ALIGNMENT_MIDDLE, - 0}, - "REJECT", - 0, - 0xB7B7B7, - COLOR_BG_1, - io_seproxyhal_touch_address_cancel, - NULL, - NULL}, - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0, 18, - BAGL_FILL, 0x41ccb4, COLOR_BG_1, - BAGL_FONT_OPEN_SANS_REGULAR_11_14PX | BAGL_FONT_ALIGNMENT_CENTER | - BAGL_FONT_ALIGNMENT_MIDDLE, - 0}, - "CONFIRM", - 0, - 0x3ab7a2, - COLOR_BG_1, - io_seproxyhal_touch_address_ok, - NULL, - NULL}, -}; - -unsigned int ui_address_blue_prepro(const bagl_element_t *element) { - if (element->component.userid > 0) { - unsigned int length = strlen(ctx.req.pk.address); - if (length >= (element->component.userid & 0xF) * MAX_CHAR_PER_LINE) { - os_memset(displayString, 0, MAX_CHAR_PER_LINE + 1); - os_memmove( - displayString, - ctx.req.pk.address + (element->component.userid & 0xF) * MAX_CHAR_PER_LINE, - MIN(length - (element->component.userid & 0xF) * MAX_CHAR_PER_LINE, MAX_CHAR_PER_LINE)); - return 1; - } - // nothing to draw for this line - return 0; - } - return 1; -} -unsigned int ui_address_blue_button(unsigned int button_mask, - unsigned int button_mask_counter) { - return 0; -} -#endif // #if defined(TARGET_BLUE) - -#if defined(TARGET_NANOS) -const bagl_element_t ui_address_nanos[] = { - // { - // {type, userid, x, y, width, height, stroke, radius, fill, fgcolor, - // bgcolor, font_id, icon_id}, - // text, - // touch_area_brim, - // overfgcolor, - // overbgcolor, - // tap, - // out, - // over} - // } - - {{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, - 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, - BAGL_GLYPH_ICON_CROSS}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, - BAGL_GLYPH_ICON_CHECK}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x01, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Confirm", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x01, 16, 26, 96, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - "address", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x02, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Address", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - ctx.req.pk.address, - 0, - 0, - 0, - NULL, - NULL, - NULL}, -}; - -unsigned int ui_address_prepro(const bagl_element_t *element) { - if (element->component.userid > 0) { - unsigned int display = (ux_step == element->component.userid - 1); - if (display) { - if (element->component.userid == 0x01) { - UX_CALLBACK_SET_INTERVAL(2000); - } - if (element->component.userid == 0x02) { - UX_CALLBACK_SET_INTERVAL(MAX(2000, 1000 + bagl_label_roundtrip_duration_ms(element, 7))); - } - } - return display; - } - return 1; -} - -unsigned int ui_address_nanos_button(unsigned int button_mask, - unsigned int button_mask_counter); -#endif // #if defined(TARGET_NANOS) - -#if defined(TARGET_BLUE) - -char *ui_details_title; -char *ui_details_content; -typedef void (*callback_t)(void); -callback_t ui_details_back_callback; - -const bagl_element_t * ui_details_blue_back_callback(const bagl_element_t *element) { - ui_details_back_callback(); - return 0; -} - -const bagl_element_t ui_details_blue[] = { - // type userid x y w h str rad - // fill fg bg fid iid txt touchparams... ] - - // erase screen (only under the status bar) - {{BAGL_RECTANGLE, 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE, 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE, 0x01, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, - BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0}, - displayString, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 50, 44, 0, 0, - BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT, - BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_CENTER | - BAGL_FONT_ALIGNMENT_MIDDLE, - 0}, - BAGL_FONT_SYMBOLS_0_LEFT, - 0, - COLOR_APP, - 0xFFFFFF, - ui_details_blue_back_callback, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, - "VALUE", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x10, 30, 136, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0}, - displayString, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x11, 30, 159, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0}, - displayString, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x12, 30, 182, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0}, - displayString, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x00, 0, 450, 320, 14, 0, 0, 0, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Review the whole value before continuing.", - 10, - 0, - COLOR_BG_1, - NULL, - NULL, - NULL} -}; - -unsigned int ui_details_blue_prepro(const bagl_element_t *element) { - if (element->component.userid == 1) { - strcpy(displayString, (const char *)PIC(ui_details_title)); - } else if (element->component.userid > 0) { - unsigned int length = strlen(ui_details_content); - if (length >= (element->component.userid & 0xF) * MAX_CHAR_PER_LINE) { - os_memset(displayString, 0, MAX_CHAR_PER_LINE + 1); - os_memmove( - displayString, - ui_details_content + (element->component.userid & 0xF) * MAX_CHAR_PER_LINE, - MIN(length - (element->component.userid & 0xF) * MAX_CHAR_PER_LINE, MAX_CHAR_PER_LINE)); - return 1; - } - // nothing to draw for this line - return 0; - } - return 1; -} - -unsigned int ui_details_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { - return 0; -} - -void ui_approval_blue_init(void); - -bagl_element_callback_t ui_approval_blue_ok; -bagl_element_callback_t ui_approval_blue_cancel; - -const bagl_element_t *ui_approval_blue_ok_callback(const bagl_element_t *e) { - return ui_approval_blue_ok(e); -} - -const bagl_element_t * ui_approval_blue_cancel_callback(const bagl_element_t *e) { - return ui_approval_blue_cancel(e); -} - -const char *ui_approval_blue_values[7]; - -const char *const ui_approval_blue_details_name[][7] = { - { "FEE", "VALIDITY START", NULL, NULL, NULL, "RECIPIENT", "AMOUNT" }, - { "FEE", "VALIDITY START", "SENDER", "DATA", "RECIPIENT", "AMOUNT", NULL } -}; - -const bagl_element_t *ui_approval_common_show_details(unsigned int detailidx) { - if (ui_approval_blue_values[detailidx] != NULL && - strlen(ui_approval_blue_values[detailidx]) * BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH >= 160) { - // display details screen - ui_details_title = ui_approval_blue_details_name[ctx.req.tx.content.operationType][detailidx]; - ui_details_content = ui_approval_blue_values[detailidx]; - ui_details_back_callback = ui_approval_blue_init; - UX_DISPLAY(ui_details_blue, ui_details_blue_prepro); - } - return NULL; -} - -const bagl_element_t *ui_approval_blue_fee_details(const bagl_element_t *e) { - return ui_approval_common_show_details(0); -} - -const bagl_element_t *ui_approval_blue_validity_details(const bagl_element_t *e) { - return ui_approval_common_show_details(1); -} - -const bagl_element_t *ui_approval_blue_1_details(const bagl_element_t *e) { - return ui_approval_common_show_details(2); -} - -const bagl_element_t *ui_approval_blue_2_details(const bagl_element_t *e) { - return ui_approval_common_show_details(3); -} - -const bagl_element_t *ui_approval_blue_3_details(const bagl_element_t *e) { - return ui_approval_common_show_details(4); -} - -const bagl_element_t *ui_approval_blue_recipient_details(const bagl_element_t *e) { - return ui_approval_common_show_details(5); -} - -const bagl_element_t *ui_approval_blue_amount_details(const bagl_element_t *e) { - return ui_approval_common_show_details(6); -} - -const bagl_element_t ui_approval_blue[] = { - {{BAGL_RECTANGLE, 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1, 0x000000, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // erase screen (only under the status bar) - {{BAGL_RECTANGLE, 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP, COLOR_APP, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - /// TOP STATUS BAR - {{BAGL_LABELINE, 0x60, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP, BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "CONFIRM OPERATION", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // BADGE_TRANSACTION.GIF - {{BAGL_ICON, 0x40, 30, 98, 50, 50, 0, 0, BAGL_FILL, 0, COLOR_BG_1, 0, 0}, - &C_badge_transaction, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x50, 100, 117, 320, 30, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0}, - operationCaption, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x00, 100, 138, 320, 30, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, - subtitleCaption, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // Recipient - {{BAGL_LABELINE, 0x75, 30, 179, 120, 20, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - // x-18 when ... - {{BAGL_LABELINE, 0x15, 130, 179, 160, 20, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - 0, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x25, 284, 179, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - BAGL_FONT_SYMBOLS_0_MINIRIGHT, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x80, 0, 158, 320, 34, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0, 0}, - NULL, - 0, - 0xEEEEEE, - 0x000000, - ui_approval_blue_recipient_details, - ui_menu_item_out_over, - ui_menu_item_out_over}, - {{BAGL_RECTANGLE, 0x25, 0, 158, 5, 34, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0x41CCB4, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE, 0x35, 30, 192, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // Amount - {{BAGL_LABELINE, 0x76, 30, 214, 100, 23, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - // x-18 when ... - {{BAGL_LABELINE, 0x16, 130, 214, 160, 23, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x26, 284, 214, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - BAGL_FONT_SYMBOLS_0_MINIRIGHT, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x80, 0, 193, 320, 34, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0, 0}, - NULL, - 0, - 0xEEEEEE, - 0x000000, - ui_approval_blue_amount_details, - ui_menu_item_out_over, - ui_menu_item_out_over}, - {{BAGL_RECTANGLE, 0x26, 0, 193, 5, 34, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0x41CCB4, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE, 0x36, 30, 227, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // Fee - {{BAGL_LABELINE, 0x70, 30, 249, 120, 20, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - // x-18 when ... - {{BAGL_LABELINE, 0x10, 130, 249, 160, 20, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x20, 284, 249, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - BAGL_FONT_SYMBOLS_0_MINIRIGHT, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x80, 0, 228, 320, 34, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0, 0}, - NULL, - 0, - 0xEEEEEE, - 0x000000, - ui_approval_blue_fee_details, - ui_menu_item_out_over, - ui_menu_item_out_over}, - {{BAGL_RECTANGLE, 0x20, 0, 228, 5, 34, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0x41CCB4, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE, 0x31, 30, 262, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // Validity Start - {{BAGL_LABELINE, 0x71, 30, 284, 100, 23, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - // x-18 when ... - {{BAGL_LABELINE, 0x11, 130, 284, 160, 23, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x21, 284, 284, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - BAGL_FONT_SYMBOLS_0_MINIRIGHT, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x80, 0, 263, 320, 34, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0, 0}, - NULL, - 0, - 0xEEEEEE, - 0x000000, - ui_approval_blue_validity_details, - ui_menu_item_out_over, - ui_menu_item_out_over}, - {{BAGL_RECTANGLE, 0x21, 0, 263, 5, 34, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0x41CCB4, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE, 0x32, 30, 297, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // Details 1 - {{BAGL_LABELINE, 0x72, 30, 319, 100, 23, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - // x-18 when ... - {{BAGL_LABELINE, 0x12, 130, 319, 160, 23, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x22, 284, 319, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - BAGL_FONT_SYMBOLS_0_MINIRIGHT, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x80, 0, 298, 320, 34, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0, 0}, - NULL, - 0, - 0xEEEEEE, - 0x000000, - ui_approval_blue_1_details, - ui_menu_item_out_over, - ui_menu_item_out_over}, - {{BAGL_RECTANGLE, 0x22, 0, 298, 5, 34, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0x41CCB4, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE, 0x33, 30, 332, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // Details 2 - {{BAGL_LABELINE, 0x73, 30, 354, 100, 23, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - // x-18 when ... - {{BAGL_LABELINE, 0x13, 130, 354, 160, 23, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x23, 284, 354, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - BAGL_FONT_SYMBOLS_0_MINIRIGHT, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x80, 0, 333, 320, 34, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0, 0}, - NULL, - 0, - 0xEEEEEE, - 0x000000, - ui_approval_blue_2_details, - ui_menu_item_out_over, - ui_menu_item_out_over}, - {{BAGL_RECTANGLE, 0x23, 0, 333, 5, 34, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0x41CCB4, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE, 0x34, 30, 367, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - // Details 3 - {{BAGL_LABELINE, 0x74, 30, 389, 100, 23, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - // x-18 when ... - {{BAGL_LABELINE, 0x14, 130, 389, 160, 23, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x24, 284, 389, 6, 16, 0, 0, BAGL_FILL, 0x999999, COLOR_BG_1, BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_RIGHT, 0}, - BAGL_FONT_SYMBOLS_0_MINIRIGHT, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x80, 0, 368, 320, 34, 0, 9, BAGL_FILL, 0xFFFFFF, 0x000000, 0, 0}, - NULL, - 0, - 0xEEEEEE, - 0x000000, - ui_approval_blue_3_details, - ui_menu_item_out_over, - ui_menu_item_out_over}, - {{BAGL_RECTANGLE, 0x24, 0, 368, 5, 34, 0, 0, BAGL_FILL, COLOR_BG_1, COLOR_BG_1, 0, 0}, - NULL, - 0, - 0x41CCB4, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0, 18, - BAGL_FILL, 0xCCCCCC, COLOR_BG_1, - BAGL_FONT_OPEN_SANS_REGULAR_11_14PX | BAGL_FONT_ALIGNMENT_CENTER | - BAGL_FONT_ALIGNMENT_MIDDLE, - 0}, - "REJECT", - 0, - 0xB7B7B7, - COLOR_BG_1, - ui_approval_blue_cancel_callback, - NULL, - NULL}, - {{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0, 18, - BAGL_FILL, 0x41ccb4, COLOR_BG_1, - BAGL_FONT_OPEN_SANS_REGULAR_11_14PX | BAGL_FONT_ALIGNMENT_CENTER | - BAGL_FONT_ALIGNMENT_MIDDLE, - 0}, - "CONFIRM", - 0, - 0x3ab7a2, - COLOR_BG_1, - ui_approval_blue_ok_callback, - NULL, - NULL}, - -}; - -const bagl_element_t *ui_approval_blue_prepro(const bagl_element_t *element) { - if (element->component.userid == 0) { - return 1; - } - // none elements are skipped - if ((element->component.type & (~BAGL_FLAG_TOUCHABLE)) == BAGL_NONE) { - return 0; - } else { - switch (element->component.userid & 0xF0) { - // touchable nonetype entries, skip them to avoid latencies (to be fixed in the sdk later on) - case 0x80: - return 0; - - // icon - case 0x40: - // TITLE - case 0x60: - // SUBLINE - case 0x50: - return 1; - - // details label - case 0x70: - if (!ui_approval_blue_details_name[ctx.req.tx.content.operationType][element->component.userid & 0xF]) { - return NULL; - } - os_memmove(&tmp_element, element, sizeof(bagl_element_t)); - tmp_element.text = ui_approval_blue_details_name[ctx.req.tx.content.operationType][element->component.userid & 0xF]; - return &tmp_element; - - // detail value - case 0x10: - if (!ui_approval_blue_details_name[ctx.req.tx.content.operationType][element->component.userid & 0xF]) { - return NULL; - } - os_memmove(&tmp_element, element, sizeof(bagl_element_t)); - tmp_element.text = ui_approval_blue_values[(element->component.userid & 0xF)]; +void on_rejected(); +void on_address_approved(); +void on_transaction_approved(); +void on_message_approved(); +static error_t set_result_get_public_key(uint8_t *destination, uint16_t destination_length, uint16_t *out_data_length); + +/** + * Send off a response APDU for an async request previously initiated in an io_exchange with flag IO_ASYNCH_REPLY. For + * this purpose, the reply is sent with the IO_RETURN_AFTER_TX flag. + */ +static void io_finalize_async_reply(uint8_t *data, uint16_t data_length, sw_t sw) { + if (data_length > sizeof(G_io_apdu_buffer) - /* for sw */ 2) { + sw = SW_WRONG_DATA_LENGTH; + } + if (sw != SW_OK) { + // Enforce only sending an error code. + data_length = 0; + } + if (data_length && data != G_io_apdu_buffer) { + memmove(G_io_apdu_buffer, data, data_length); + } + // Append status word + G_io_apdu_buffer[data_length] = (uint8_t) (sw >> 8); + G_io_apdu_buffer[data_length + 1] = (uint8_t) (sw); + // Send APDU with flag IO_RETURN_AFTER_TX, in response to a previous call with IO_ASYNCH_REPLY. Note that an + // io_exchange call with IO_ASYNCH_REPLY skips sending any data, which we now make up for by submitting the data at + // a later time. On the other hand, IO_RETURN_AFTER_TX returns after the data transmission, and skips waiting for a + // new command APDU, which the previous io_exchange call with IO_ASYNCH_REPLY is already waiting for. The command + // loop will continue at the previous IO_ASYNCH_REPLY io_exchange call, once a new command APDU is received. This + // way, both io_exchange calls work nicely hand in hand. + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, data_length + /* for sw */ 2); +} + +void on_rejected() { + PRINTF("User rejected the request.\n"); + io_finalize_async_reply(NULL, 0, SW_DENY); +} + +void on_address_approved() { + sw_t sw = SW_OK; + uint16_t data_length = 0; + ON_ERROR( + set_result_get_public_key(G_io_apdu_buffer, sizeof(G_io_apdu_buffer), &data_length), + { sw = ERROR_TO_SW(); } + ); + io_finalize_async_reply(G_io_apdu_buffer, data_length, sw); +} + +void on_transaction_approved() { + sw_t sw = SW_OK; + uint16_t data_length = 0; - // x -= 18 when overflow is detected - if (strlen(ui_approval_blue_values[(element->component.userid & 0xF)]) * BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH >= 160) { - tmp_element.component.x -= 18; - } - return &tmp_element; - - // right arrow and left selection rectangle - case 0x20: - if (!ui_approval_blue_details_name[ctx.req.tx.content.operationType][element->component.userid & 0xF]) { - return NULL; - } - if (strlen(ui_approval_blue_values[(element->component.userid & 0xF)]) * BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH < 160) { - return NULL; - } - - // horizontal delimiter - case 0x30: - return ui_approval_blue_details_name[ctx.req.tx.content.operationType][element->component.userid & 0xF] != NULL - ? element - : NULL; + // initialize private key + uint8_t privateKeyData[64]; // the private key is only 32 bytes, but os_derive_bip32_with_seed_no_throw expects 64 + cx_ecfp_256_private_key_t privateKey; + GOTO_ON_ERROR( + os_derive_bip32_with_seed_no_throw( + /* derivation mode */ HDW_ED25519_SLIP10, + /* curve */ CX_CURVE_Ed25519, + /* path */ ctx.req.tx.bip32Path, + /* path length */ ctx.req.tx.bip32PathLength, + /* out */ privateKeyData, + /* chain code */ NULL, + /* seed key */ NULL, // use the default for HDW_ED25519_SLIP10, which is "ed25519 seed" + /* seed key length */ 0 + ) + || cx_ecfp_init_private_key_no_throw( + /* curve */ CX_CURVE_Ed25519, + /* raw key */ privateKeyData, + /* key length */ 32, + /* out */ &privateKey + ), + end, + sw, + SW_CRYPTOGRAPHY_FAIL, + "Failed to derive private key\n" + ); + // The private key data is also cleared at the end, but for extra paranoia, clear it as soon as possible. + explicit_bzero(privateKeyData, sizeof(privateKeyData)); + + // For incoming staking transactions which are meant to include a staker signature proof in their recipient data but + // only include the empty default proof, we replace that empty signature proof with an actually signed staker proof. + // For this, the same ledger account is used as staker and transaction sender, which is the most common case for + // regular users. This way, no separate signature request needs to be sent to the ledger to first create the staker + // signature proof, but both signatures are created in a single request for better UX. For advanced users, usage of + // a different staker than the transaction sender is still supported by providing a ready pre-signed signature proof + // in the request instead of an empty signature proof. Note that the staker signature proof is supposed to be signed + // on the transaction data with the empty signature proof, see verify_transaction_signature for incoming set to true + // in primitives/transaction/src/account/staking_contract/structs.rs, which is why we can conveniently use presence + // of the empty signature proof to detect, that we should create the staker signature before signing the transaction + // and directly sign it over the passed transaction with the empty proof. + bool created_staker_signature = false; + if (PARSED_TX.transaction_type == TRANSACTION_TYPE_STAKING_INCOMING + && PARSED_TX_STAKING_INCOMING.has_validator_or_staker_signature_proof + && is_empty_default_signature_proof(PARSED_TX_STAKING_INCOMING.validator_or_staker_signature_proof) + ) { + // Create the staker signature over the transaction with the empty signature proof in its data, which is exactly + // what the staker signatue must sign. Write the signature to a temporary buffer, instead of directly to the + // signature proof, to avoid writing to the data that is currently being signed. To save some stack space, we + // use G_io_apdu_buffer as that temporary buffer. + GOTO_ON_ERROR( + // As specified in datatracker.ietf.org/doc/html/rfc8032#section-5.1.6, we're using CX_SHA512 as internal + // hash algorithm for the ed25519 signature. According to the specification, there is no length restriction + // for the data. The signature has a fixed size of 64 bytes. + cx_eddsa_sign_no_throw( + /* private key */ &privateKey, + /* hash id */ CX_SHA512, + /* hash */ ctx.req.tx.rawTx, + /* hash length */ ctx.req.tx.rawTxLength, + /* out */ G_io_apdu_buffer, + /* out length */ 64 + ), + end, + sw, + SW_CRYPTOGRAPHY_FAIL, + "Failed to sign\n" + ); + // Overwrite the signature in the signature proof in rawTx via pointers in validator_or_staker_signature_proof + // which point to the original buffer. + memmove(PARSED_TX_STAKING_INCOMING.validator_or_staker_signature_proof.signature, G_io_apdu_buffer, 64); + created_staker_signature = true; + + // Similarly, overwrite the public key in the signature proof with the ledger account public key as staker, with + // G_io_apdu_buffer as temporary buffer again. Check with a compile time assertion that it can fit the temp data + _Static_assert( + sizeof(cx_ecfp_256_public_key_t) <= sizeof(G_io_apdu_buffer), + "G_io_apdu_buffer does not fit public key\n" + ); + cx_ecfp_256_public_key_t *temporary_public_key_pointer = (cx_ecfp_256_public_key_t*) G_io_apdu_buffer; + GOTO_ON_ERROR( + cx_ecfp_generate_pair_no_throw( + /* curve */ CX_CURVE_Ed25519, + /* out */ temporary_public_key_pointer, + /* private key */ &privateKey, + /* keep private key */ true + ), + end, + sw, + SW_CRYPTOGRAPHY_FAIL, + "Failed to generate public key\n" + ); + // copy public key little endian to big endian + for (uint8_t i = 0; i < 32; i++) { + PARSED_TX_STAKING_INCOMING.validator_or_staker_signature_proof.public_key[i] = + temporary_public_key_pointer->W[64 - i]; } - } - return element; -} -unsigned int ui_approval_blue_button(unsigned int button_mask, unsigned int button_mask_counter) { - return 0; -} - -void ui_approval_blue_init(void) { - UX_DISPLAY(ui_approval_blue, ui_approval_blue_prepro); -} - -void ui_approve_tx_blue_init(void) { - ui_approval_blue_ok = (bagl_element_callback_t)io_seproxyhal_touch_tx_ok; - ui_approval_blue_cancel = (bagl_element_callback_t)io_seproxyhal_touch_tx_cancel; - os_memset(ui_approval_blue_values, 0, sizeof(ui_approval_blue_values)); - ui_approval_blue_values[0] = ctx.req.tx.content.fee; - ui_approval_blue_values[1] = ctx.req.tx.content.validity_start; - ui_approval_blue_values[2] = ctx.req.tx.content.details1; - ui_approval_blue_values[3] = ctx.req.tx.content.details2; - ui_approval_blue_values[4] = ctx.req.tx.content.details3; - ui_approval_blue_values[5] = ctx.req.tx.content.recipient; - ui_approval_blue_values[6] = ctx.req.tx.content.value; - strcpy(subtitleCaption, ctx.req.tx.content.network); - strcpy(subtitleCaption + strlen(ctx.req.tx.content.network), " Network"); - ui_approval_blue_init(); -} - -#endif // #if defined(TARGET_BLUE) - - -// component id steps for different types of operations -const uint8_t ui_elements_map[][MAX_UI_STEPS] = { - { 0x01, 0x02, 0x04, 0x05, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, // basic tx - { 0x01, 0x02, 0x04, 0x05, 0x07, 0x08, 0x03, 0x00, 0x00, 0x00, 0x00 }, // basic tx + extra data - { 0x01, 0x02, 0x04, 0x05, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, // cashlink tx - { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11 } // for future use in extended tx, not yet supported -}; - -#if defined(TARGET_NANOS) - -unsigned int ui_tx_approval_prepro(const bagl_element_t *element) { - unsigned int display = 1; - if (element->component.userid > 0) { - display = (ui_elements_map[ctx.req.tx.content.operationType][ux_step] == element->component.userid); - if (display) { - UX_CALLBACK_SET_INTERVAL(MAX(2000, 1000 + bagl_label_roundtrip_duration_ms(element, 7))); + if (temporary_public_key_pointer->W[32] & 1) { + PARSED_TX_STAKING_INCOMING.validator_or_staker_signature_proof.public_key[31] |= 0x80; } } - return display; -} - -const bagl_element_t ui_approve_tx_nanos[] = { - // { - // {type, userid, x, y, width, height, stroke, radius, fill, fgcolor, - // bgcolor, font_id, icon_id}, - // text, - // touch_area_brim, - // overfgcolor, - // overbgcolor, - // tap, - // out, - // over} - // } - - {{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF, - 0, 0}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0, - BAGL_GLYPH_ICON_CROSS}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0, - BAGL_GLYPH_ICON_CHECK}, - NULL, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - //{{BAGL_ICON , 0x01, 21, 9, 14, 14, 0, 0, 0 - //, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_TRANSACTION_BADGE }, NULL, 0, 0, - //0, NULL, NULL, NULL }, - {{BAGL_LABELINE, 0x01, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Confirm", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x01, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "operation", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x02, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Operation Type", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x02, 16, 26, 96, 12, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - (char*) operationCaption, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x03, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Recipient", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x03, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - ctx.req.tx.content.recipient, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x04, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Amount", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x04, 16, 26, 96, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - ctx.req.tx.content.value, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x05, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Fee", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x05, 16, 26, 96, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - ctx.req.tx.content.fee, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x06, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Validity Start", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x06, 16, 26, 96, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - ctx.req.tx.content.validity_start, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x07, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "Network", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x07, 16, 26, 96, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - ctx.req.tx.content.network, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x08, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - (char*) details1Caption, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x08, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - ctx.req.tx.content.details1, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x09, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - (char*) details2Caption, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x09, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - ctx.req.tx.content.details2, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x10, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - (char*) details3Caption, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x10, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - ctx.req.tx.content.details3, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x11, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - (char*) details4Caption, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x1, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26}, - ctx.req.tx.content.details4, - 0, - 0, - 0, - NULL, - NULL, - NULL}, - - {{BAGL_LABELINE, 0x20, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "No details", - 0, - 0, - 0, - NULL, - NULL, - NULL}, - {{BAGL_LABELINE, 0x20, 23, 26, 82, 32, 0, 0, 0, 0xFFFFFF, 0x000000, - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0}, - "available", - 0, - 0, - 0, - NULL, - NULL, - NULL} - -}; -#endif // #if defined(TARGET_NANOS) - -#if defined(TARGET_NANOX) || defined(TARGET_NANOS2) -////////////////////////////////////////////////////////////////////// -UX_STEP_NOCB( - ux_idle_flow_1_step, - nn, - { - "Application", - "is ready", - }); -UX_STEP_NOCB( - ux_idle_flow_3_step, - bn, - { - "Version", - APPVERSION, - }); -UX_STEP_VALID( - ux_idle_flow_4_step, - pb, - os_sched_exit(-1), - { - &C_icon_dashboard_x, - "Quit", - }); -UX_FLOW(ux_idle_flow, - &ux_idle_flow_1_step, - &ux_idle_flow_3_step, - &ux_idle_flow_4_step -); - -////////////////////////////////////////////////////////////////////// - -void display_next_state(bool is_upper_border); - -char caption[18]; -char details[MAX_DATA_STRING_LENGTH]; - -UX_STEP_NOCB(ux_confirm_flow_1_step, - pnn, - { - &C_icon_eye, - "Confirm", - "operation", - }); -UX_STEP_INIT( - ux_init_upper_border, - NULL, - NULL, - { - display_next_state(true); - }); -UX_STEP_NOCB( - ux_variable_display, - bnnn_paging, - { - .title = caption, - .text = details, - }); -UX_STEP_INIT( - ux_init_lower_border, - NULL, - NULL, - { - display_next_state(false); - }); -UX_STEP_VALID( - ux_confirm_flow_5_step, - pbb, - io_seproxyhal_touch_tx_ok(NULL), - { - &C_icon_validate_14, - "Accept", - "and send", - }); -UX_STEP_VALID( - ux_confirm_flow_6_step, - pb, - io_seproxyhal_touch_tx_cancel(NULL), - { - &C_icon_crossmark, - "Reject", - }); -// confirm: confirm transaction / Amount: fullAmount / Address: fullAddress / Fees: feesAmount -UX_FLOW(ux_confirm_flow, - &ux_confirm_flow_1_step, - - &ux_init_upper_border, - &ux_variable_display, - &ux_init_lower_border, - - &ux_confirm_flow_5_step, - &ux_confirm_flow_6_step -); - - -uint8_t num_data; -volatile uint8_t current_data_index; -volatile uint8_t current_state; - -#define INSIDE_BORDERS 0 -#define OUT_OF_BORDERS 1 - -void set_state_data() { - - switch (current_data_index) - { - case 0: - strncpy(caption, "Operation Type", sizeof(caption)); - strncpy(details, operationCaption, sizeof(details)); - break; - - case 1: - strncpy(caption, "Recipient", sizeof(caption)); - strncpy(details, ctx.req.tx.content.recipient, sizeof(details)); - break; - - case 2: - strncpy(caption, "Amount", sizeof(caption)); - strncpy(details, ctx.req.tx.content.value, sizeof(details)); - break; - - case 3: - strncpy(caption, "Fee", sizeof(caption)); - strncpy(details, ctx.req.tx.content.fee, sizeof(details)); - break; - - case 4: - strncpy(caption, "Validity Start", sizeof(caption)); - strncpy(details, ctx.req.tx.content.validity_start, sizeof(details)); - break; - - case 5: - strncpy(caption, "Network", sizeof(caption)); - strncpy(details, ctx.req.tx.content.network, sizeof(details)); - break; - - case 6: - // set details 1 - memcpy(caption, details1Caption, sizeof(caption)); - memcpy(details, ctx.req.tx.content.details1, MAX_DATA_STRING_LENGTH); - break; - case 7: - // set details 2 - memcpy(caption, details2Caption, sizeof(caption)); - memcpy(details, ctx.req.tx.content.details2, MAX_DATA_STRING_LENGTH); - break; - - case 8: - // set details 3 - memcpy(caption, details3Caption, sizeof(caption)); - memcpy(details, ctx.req.tx.content.details3, MAX_DATA_STRING_LENGTH); - break; - - case 9: - // set details 4 - memcpy(caption, details4Caption, sizeof(caption)); - memcpy(details, ctx.req.tx.content.details4, MAX_DATA_STRING_LENGTH); - break; - - default: - THROW(0x6666); - break; - } - -} - -void display_next_state(bool is_upper_border){ - - if(is_upper_border){ // walking over the first border - if(current_state == OUT_OF_BORDERS){ - current_state = INSIDE_BORDERS; - set_state_data(); - ux_flow_next(); - } - else{ - if(current_data_index>0){ - current_data_index--; - set_state_data(); - ux_flow_next(); - } - else{ - current_state = OUT_OF_BORDERS; - current_data_index = 0; - ux_flow_prev(); - } - } - } - else // walking over the second border - { - if(current_state == OUT_OF_BORDERS){ - current_state = INSIDE_BORDERS; - set_state_data(); - ux_flow_prev(); - } - else{ - if(num_data != 0 && current_data_index= 8 - tx = cx_eddsa_sign(&privateKey, CX_LAST, CX_SHA512, ctx.req.tx.rawTx, ctx.req.tx.rawTxLength, NULL, 0, G_io_apdu_buffer, sizeof(G_io_apdu_buffer), NULL); -#else - tx = cx_eddsa_sign(&privateKey, NULL, CX_LAST, CX_SHA512, ctx.req.tx.rawTx, ctx.req.tx.rawTxLength, G_io_apdu_buffer); -#endif - os_memset(&privateKey, 0, sizeof(privateKey)); - - G_io_apdu_buffer[tx++] = 0x90; - G_io_apdu_buffer[tx++] = 0x00; - - // Send back the response, do not restart the event loop - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); - - // Display back the original UX - ui_idle(); - - return 0; // do not redraw the widget -} - -unsigned int io_seproxyhal_touch_tx_cancel(const bagl_element_t *e) { - G_io_apdu_buffer[0] = 0x69; - G_io_apdu_buffer[1] = 0x85; - - // Send back the response, do not restart the event loop - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); - - // Display back the original UX - ui_idle(); - return 0; // do not redraw the widget -} - - -uint8_t countSteps(uint8_t operationType) { - uint8_t i; - for (i = 0; i < MAX_UI_STEPS; i++) { - if (ui_elements_map[operationType][i] == 0x00) { - return i; - } - } - return MAX_UI_STEPS; -} - -#if defined(TARGET_NANOS) -unsigned int ui_approve_tx_nanos_button(unsigned int button_mask, unsigned int button_mask_counter) { - switch (button_mask) { - case BUTTON_EVT_RELEASED | BUTTON_LEFT: - io_seproxyhal_touch_tx_cancel(NULL); - break; - - case BUTTON_EVT_RELEASED | BUTTON_RIGHT: { - io_seproxyhal_touch_tx_ok(NULL); - break; - } - } - return 0; -} - - -#endif // #if defined(TARGET_NANOS) - -unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { - switch (channel & ~(IO_FLAGS)) { - case CHANNEL_KEYBOARD: - break; - - // multiplexed io exchange over a SPI channel and TLV encapsulated protocol - case CHANNEL_SPI: - if (tx_len) { - io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len); - - if (channel & IO_RESET_AFTER_REPLIED) { - reset(); - } - return 0; // nothing received from the master so far (it's a tx - // transaction) - } else { - return io_seproxyhal_spi_recv(G_io_apdu_buffer, sizeof(G_io_apdu_buffer), 0); - } - - default: - THROW(INVALID_PARAMETER); - } - return 0; + uint8_t privateKeyData[64]; // the private key is only 32 bytes, but os_derive_bip32_with_seed_no_throw expects 64 + cx_ecfp_256_private_key_t privateKey; + ON_ERROR( + os_derive_bip32_with_seed_no_throw( + /* derivation mode */ HDW_ED25519_SLIP10, + /* curve */ CX_CURVE_Ed25519, + /* path */ ctx.req.msg.bip32Path, + /* path length */ ctx.req.msg.bip32PathLength, + /* out */ privateKeyData, + /* chain code */ NULL, + /* seed key */ NULL, // use the default for HDW_ED25519_SLIP10, which is "ed25519 seed" + /* seed key length */ 0 + ) + || cx_ecfp_init_private_key_no_throw( + /* curve */ CX_CURVE_Ed25519, + /* raw key */ privateKeyData, + /* key length */ 32, + /* out */ &privateKey + ) + // Sign hashed message. + // As specified in datatracker.ietf.org/doc/html/rfc8032#section-5.1.6, we're using CX_SHA512 as internal hash + // algorithm for the ed25519 signature. According to the specification, there is no length restriction for the + // data. The signature has a fixed size of 64 bytes. + || cx_eddsa_sign_no_throw( + /* private key */ &privateKey, + /* hash id */ CX_SHA512, + /* hash */ ctx.req.msg.confirm.prefixedMessageHash, + /* hash length */ sizeof(ctx.req.msg.confirm.prefixedMessageHash), + /* out */ G_io_apdu_buffer, + /* out length */ 64 + ), + { + sw = error; + data_length = 0; + }, + SW_CRYPTOGRAPHY_FAIL, + "Failed to derive private key or to sign\n" + ); + explicit_bzero(privateKeyData, sizeof(privateKeyData)); + explicit_bzero(&privateKey, sizeof(privateKey)); + + io_finalize_async_reply(G_io_apdu_buffer, data_length, sw); +} + +void u2f_send_keep_alive() { + PRINTF("Send U2F heartbeat\n"); + ctx.u2fTimer = 0; + io_finalize_async_reply(NULL, 0, SW_KEEP_ALIVE); } +WARN_UNUSED_RESULT +static error_t set_result_get_public_key(uint8_t *destination, uint16_t destination_length, uint16_t *out_data_length) { + *out_data_length = 0; + RETURN_ON_ERROR( + destination_length < /* public key */ 32 + /* verification signature */ (ctx.req.pk.returnSignature ? 64 : 0), + ERROR_INVALID_LENGTH, + "Buffer too short to fit public key or verification signature\n" + ); -uint32_t set_result_get_publicKey() { - uint32_t tx = 0; - - uint8_t publicKey[32]; - // copy public key little endian to big endian - uint8_t i; - for (i = 0; i < 32; i++) { - publicKey[i] = ctx.req.pk.publicKey.W[64 - i]; + // Copy public key little endian to big endian + for (uint8_t i = 0; i < 32; i++) { + destination[i] = ctx.req.pk.publicKey.W[64 - i]; } if ((ctx.req.pk.publicKey.W[32] & 1) != 0) { - publicKey[31] |= 0x80; + destination[31] |= 0x80; } + *out_data_length += 32; - os_memmove(G_io_apdu_buffer + tx, publicKey, 32); - - tx += 32; - + // Add verification signature if (ctx.req.pk.returnSignature) { - os_memmove(G_io_apdu_buffer + tx, ctx.req.pk.signature, 64); - tx += 64; + memmove(destination + *out_data_length, ctx.req.pk.signature, 64); + *out_data_length += 64; } - return tx; -} - -uint8_t readBip32Path(uint8_t *dataBuffer, uint32_t *bip32Path) { - uint8_t bip32PathLength = dataBuffer[0]; - dataBuffer += 1; - if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) { - THROW(0x6a80); - } - uint8_t i; - for (i = 0; i < bip32PathLength; i++) { - bip32Path[i] = (dataBuffer[0] << 24) | (dataBuffer[1] << 16) | - (dataBuffer[2] << 8) | (dataBuffer[3]); - dataBuffer += 4; - } - return bip32PathLength; + return ERROR_NONE; } -void handleGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, volatile unsigned int *flags, volatile unsigned int *tx) { +WARN_UNUSED_RESULT +sw_t handle_get_public_key(uint8_t p1, uint8_t p2, uint8_t *data_buffer, uint16_t data_length, + uint16_t *out_apdu_length, bool *out_start_async_reply) { + *out_apdu_length = 0; + *out_start_async_reply = false; + sw_t sw = SW_OK; - if ((p1 != P1_SIGNATURE) && (p1 != P1_NO_SIGNATURE)) { - THROW(0x6B00); - } - if ((p2 != P2_CONFIRM) && (p2 != P2_NO_CONFIRM)) { - THROW(0x6B00); - } - ctx.req.pk.returnSignature = (p1 == P1_SIGNATURE); + uint8_t privateKeyData[64]; // the private key is only 32 bytes, but os_derive_bip32_with_seed_no_throw expects 64 + cx_ecfp_256_private_key_t privateKey; - uint32_t bip32Path[MAX_BIP32_PATH]; - uint8_t bip32PathLength = readBip32Path(dataBuffer, bip32Path); - dataBuffer += 1 + bip32PathLength * 4; - dataLength -= 1 + bip32PathLength * 4; + GOTO_ON_ERROR( + ((p1 != P1_SIGNATURE) && (p1 != P1_NO_SIGNATURE)) + || ((p2 != P2_CONFIRM) && (p2 != P2_NO_CONFIRM)), + end, + sw, + SW_WRONG_P1P2, + "Invalid P1 or P2\n" + ); - uint16_t msgLength; - uint8_t msg[32]; - if (ctx.req.pk.returnSignature) { - msgLength = dataLength; - if (msgLength > 32) { - THROW(0x6a80); - } - os_memmove(msg, dataBuffer, msgLength); - } - - uint8_t privateKeyData[32]; - cx_ecfp_private_key_t privateKey; - os_perso_derive_node_bip32_seed_key(HDW_ED25519_SLIP10, CX_CURVE_Ed25519, bip32Path, bip32PathLength, privateKeyData, NULL, "ed25519 seed", 12); + ctx.req.pk.returnSignature = (p1 == P1_SIGNATURE); - cx_ecfp_init_private_key(CX_CURVE_Ed25519, privateKeyData, 32, &privateKey); - os_memset(privateKeyData, 0, sizeof(privateKeyData)); - cx_ecfp_generate_pair(CX_CURVE_Ed25519, &ctx.req.pk.publicKey, &privateKey, 1); + uint32_t bip32Path[MAX_BIP32_PATH_LENGTH]; + uint8_t bip32PathLength; + GOTO_ON_ERROR( + !read_bip32_path(&data_buffer, &data_length, bip32Path, &bip32PathLength), + end, + sw, + SW_WRONG_DATA_LENGTH + ); + + // Optionally create a signature with which the public key can be verified. We only allow signing messages up to 31 + // bytes, as we're blind signing here, and longer data could be Nimiq messages, which are 32 byte Sha256 digests, or + // transactions, which have varying sizes but larger than 32 bytes. Additionally, the message must start with the + // suffix "dummy-data:", to ensure even further that no meaningful data could be signed. + uint8_t msgLength; + uint8_t *msg = NULL; if (ctx.req.pk.returnSignature) { -#if CX_APILEVEL >= 8 - cx_eddsa_sign(&privateKey, CX_LAST, CX_SHA512, msg, msgLength, NULL, 0, ctx.req.pk.signature, sizeof(ctx.req.pk.signature), NULL); -#else - cx_eddsa_sign(&privateKey, NULL, CX_LAST, CX_SHA512, msg, msgLength, ctx.req.pk.signature); -#endif - } - os_memset(&privateKey, 0, sizeof(privateKey)); + GOTO_ON_ERROR( + data_length > 31, + end, + sw, + SW_INCORRECT_DATA, + "Verification message to sign must not exceed 31 bytes\n" + ); + msgLength = (uint8_t) data_length; + GOTO_ON_ERROR( + !read_sub_buffer(data_length, &data_buffer, &data_length, &msg), + end, + sw, + SW_WRONG_DATA_LENGTH + ); + GOTO_ON_ERROR( + memcmp(msg, "dummy-data:", sizeof("dummy-data:") - /* exclude string terminator */ 1) != 0, + end, + sw, + SW_INCORRECT_DATA, + "Verification message to sign must start with prefix \"dummy-data:\"\n" + ); + } + + GOTO_ON_ERROR( + data_length != 0, + end, + sw, + SW_WRONG_DATA_LENGTH, + "INS_GET_PUBLIC_KEY instruction data too long\n" + ); + + GOTO_ON_ERROR( + os_derive_bip32_with_seed_no_throw( + /* derivation mode */ HDW_ED25519_SLIP10, + /* curve */ CX_CURVE_Ed25519, + /* path */ bip32Path, + /* path length */ bip32PathLength, + /* out */ privateKeyData, + /* chain code */ NULL, + /* seed key */ NULL, // use the default for HDW_ED25519_SLIP10, which is "ed25519 seed" + /* seed key length */ 0 + ) + || cx_ecfp_init_private_key_no_throw( + /* curve */ CX_CURVE_Ed25519, + /* raw key */ privateKeyData, + /* key length */ 32, + /* out */ &privateKey + ), + end, + sw, + SW_CRYPTOGRAPHY_FAIL, + "Failed to derive private key\n" + ); + // The private key data is also cleared at the end, but for extra paranoia, clear it as soon as possible. + explicit_bzero(privateKeyData, sizeof(privateKeyData)); + + GOTO_ON_ERROR( + cx_ecfp_generate_pair_no_throw( + /* curve */ CX_CURVE_Ed25519, + /* out */ &ctx.req.pk.publicKey, + /* private key */ &privateKey, + /* keep private key */ true + ), + end, + sw, + SW_CRYPTOGRAPHY_FAIL, + "Failed to generate public key\n" + ); + if (ctx.req.pk.returnSignature && msg) { + GOTO_ON_ERROR( + cx_eddsa_sign_no_throw( + /* private key */ &privateKey, + /* hash id */ CX_SHA512, + /* hash */ msg, + /* hash length */ msgLength, + /* out */ ctx.req.pk.signature, + /* out length */ sizeof(ctx.req.pk.signature) + ), + end, + sw, + SW_CRYPTOGRAPHY_FAIL, + "Failed to sign\n" + ); + } + // The private key is also cleared at the end, but for extra paranoia, clear it as soon as possible. + explicit_bzero(&privateKey, sizeof(privateKey)); if (p2 & P2_CONFIRM) { + // Async request, in which we display the address and ask the user to confirm. uint8_t publicKey[32]; // copy public key little endian to big endian uint8_t i; @@ -2124,308 +462,422 @@ void handleGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t da if ((ctx.req.pk.publicKey.W[32] & 1) != 0) { publicKey[31] |= 0x80; } - print_public_key(publicKey, ctx.req.pk.address); -#if defined(TARGET_BLUE) - UX_DISPLAY(ui_address_blue, ui_address_blue_prepro); -#elif defined(TARGET_NANOS) - ux_step = 0; - ux_step_count = 2; - UX_DISPLAY(ui_address_nanos, ui_address_prepro); -#elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) - ux_flow_init(0, ux_display_public_flow, NULL); -#endif // #if TARGET - *flags |= IO_ASYNCH_REPLY; + GOTO_ON_ERROR( + print_public_key_as_address(publicKey, ctx.req.pk.address), + end, + sw, + ERROR_TO_SW(), + "Failed to print public key\n" + ); + + ui_public_key(); + *out_start_async_reply = true; } else { - *tx = set_result_get_publicKey(); - THROW(0x9000); + // Sync request, in which the public key is returned without any user interaction. + GOTO_ON_ERROR( + set_result_get_public_key(G_io_apdu_buffer, sizeof(G_io_apdu_buffer), out_apdu_length), + end, + sw, + ERROR_TO_SW() + ); } -} -void handleGetAppConfiguration(volatile unsigned int *tx) { - G_io_apdu_buffer[0] = 0x00; - G_io_apdu_buffer[1] = LEDGER_MAJOR_VERSION; - G_io_apdu_buffer[2] = LEDGER_MINOR_VERSION; - G_io_apdu_buffer[3] = LEDGER_PATCH_VERSION; - *tx = 4; - THROW(0x9000); +end: + explicit_bzero(privateKeyData, sizeof(privateKeyData)); + explicit_bzero(&privateKey, sizeof(privateKey)); + return sw; } -void handleSignTx(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, volatile unsigned int *flags, volatile unsigned int *tx) { +WARN_UNUSED_RESULT +sw_t handle_sign_transaction(uint8_t p1, uint8_t p2, uint8_t *data_buffer, uint16_t data_length, + bool *out_start_async_reply) { + *out_start_async_reply = false; - if ((p1 != P1_FIRST) && (p1 != P1_MORE)) { - THROW(0x6B00); - } - if ((p2 != P2_LAST) && (p2 != P2_MORE)) { - THROW(0x6B00); - } + RETURN_ON_ERROR( + ((p1 != P1_FIRST) && (p1 != P1_MORE)) + || ((p2 != P2_LAST) && (p2 != P2_MORE)), + SW_WRONG_P1P2, + "Invalid P1 or P2\n" + ); if (p1 == P1_FIRST) { - // read the bip32 path - ctx.req.tx.bip32PathLength = readBip32Path(dataBuffer, ctx.req.tx.bip32Path); - dataBuffer += 1 + ctx.req.tx.bip32PathLength * 4; - dataLength -= 1 + ctx.req.tx.bip32PathLength * 4; + _Static_assert( + sizeof(ctx.req.tx.transactionVersion) == 1, + "transactionVersion has more than one byte. Need to take endianness into account when reading into a u8 " + "pointer.\n" + ); + RETURN_ON_ERROR( + !read_bip32_path(&data_buffer, &data_length, ctx.req.tx.bip32Path, &ctx.req.tx.bip32PathLength) + || !read_u8(&data_buffer, &data_length, &ctx.req.tx.transactionVersion), + SW_WRONG_DATA_LENGTH + ); // read raw tx data - ctx.req.tx.rawTxLength = dataLength; - if (dataLength > MAX_RAW_TX) { - THROW(0x6700); - } - os_memmove(ctx.req.tx.rawTx, dataBuffer, dataLength); + RETURN_ON_ERROR( + data_length > MAX_RAW_TX, + SW_WRONG_DATA_LENGTH, + "Transaction too long\n" + ); + ctx.req.tx.rawTxLength = data_length; + memmove(ctx.req.tx.rawTx, data_buffer, data_length); } else { // read more raw tx data uint32_t offset = ctx.req.tx.rawTxLength; - ctx.req.tx.rawTxLength += dataLength; - if (ctx.req.tx.rawTxLength > MAX_RAW_TX) { - THROW(0x6700); - } - os_memmove(ctx.req.tx.rawTx+offset, dataBuffer, dataLength); + ctx.req.tx.rawTxLength += data_length; + RETURN_ON_ERROR( + ctx.req.tx.rawTxLength > MAX_RAW_TX, + SW_WRONG_DATA_LENGTH, + "Transaction too long\n" + ); + memmove(ctx.req.tx.rawTx+offset, data_buffer, data_length); } if (p2 == P2_MORE) { - THROW(0x9000); + // Processing of current chunk finished; send success status word and let the caller continue with more chunks. + return SW_OK; } - os_memset(&ctx.req.tx.content, 0, sizeof(ctx.req.tx.content)); - parseTx(ctx.req.tx.rawTx, &ctx.req.tx.content); - - // prepare for display - os_memset((char *)operationCaption, 0, sizeof(operationCaption)); - print_caption(ctx.req.tx.content.operationType, CAPTION_TYPE_OPERATION, (char *)operationCaption); - -#ifndef TARGET_BLUE - os_memset((char *)details1Caption, 0, sizeof(details1Caption)); - os_memset((char *)details2Caption, 0, sizeof(details2Caption)); - os_memset((char *)details3Caption, 0, sizeof(details3Caption)); - os_memset((char *)details4Caption, 0, sizeof(details4Caption)); - print_caption(ctx.req.tx.content.operationType, CAPTION_TYPE_DETAILS1, (char *)details1Caption); - print_caption(ctx.req.tx.content.operationType, CAPTION_TYPE_DETAILS2, (char *)details2Caption); - print_caption(ctx.req.tx.content.operationType, CAPTION_TYPE_DETAILS3, (char *)details3Caption); - print_caption(ctx.req.tx.content.operationType, CAPTION_TYPE_DETAILS4, (char *)details4Caption); -#endif // #if TARGET - -#if defined(TARGET_BLUE) - ux_step_count = 0; - ui_approve_tx_blue_init(); -#elif defined(TARGET_NANOS) - ux_step = 0; - ux_step_count = countSteps(ctx.req.tx.content.operationType); - UX_DISPLAY(ui_approve_tx_nanos, ui_tx_approval_prepro); -#elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) - num_data = countSteps(ctx.req.tx.content.operationType); - current_data_index = 0; - current_state = OUT_OF_BORDERS; - ux_flow_init(0, ux_confirm_flow, NULL); -#endif // #if TARGET - - *flags |= IO_ASYNCH_REPLY; -} + memset(&PARSED_TX, 0, sizeof(PARSED_TX)); + RETURN_ON_ERROR( + parse_tx(ctx.req.tx.transactionVersion, ctx.req.tx.rawTx, ctx.req.tx.rawTxLength, &PARSED_TX), + ERROR_TO_SW(), + "Failed to parse transaction\n" + ); -void handleKeepAlive(volatile unsigned int *flags) { - *flags |= IO_ASYNCH_REPLY; + ui_transaction_signing(); + *out_start_async_reply = true; + return SW_OK; } -void handleApdu(volatile unsigned int *flags, volatile unsigned int *tx) { - unsigned short sw = 0; +WARN_UNUSED_RESULT +sw_t handle_sign_message(uint8_t p1, uint8_t p2, uint8_t *data_buffer, uint16_t data_length, + bool *out_start_async_reply) { + *out_start_async_reply = false; - BEGIN_TRY { - TRY { - if (os_global_pin_is_validated() != BOLOS_UX_OK) { - THROW(0x6982); - } - if (G_io_apdu_buffer[OFFSET_CLA] != CLA) { - THROW(0x6e00); - } + RETURN_ON_ERROR( + ((p1 != P1_FIRST) && (p1 != P1_MORE)) + || ((p2 != P2_LAST) && (p2 != P2_MORE)), + SW_WRONG_P1P2, + "Invalid P1 or P2\n" + ); - ctx.u2fTimer = U2F_REQUEST_TIMEOUT; - - switch (G_io_apdu_buffer[OFFSET_INS]) { - case INS_GET_PUBLIC_KEY: - handleGetPublicKey(G_io_apdu_buffer[OFFSET_P1], - G_io_apdu_buffer[OFFSET_P2], - G_io_apdu_buffer + OFFSET_CDATA, - G_io_apdu_buffer[OFFSET_LC], - flags, tx); - break; - - case INS_SIGN_TX: - handleSignTx(G_io_apdu_buffer[OFFSET_P1], - G_io_apdu_buffer[OFFSET_P2], - G_io_apdu_buffer + OFFSET_CDATA, - G_io_apdu_buffer[OFFSET_LC], flags, tx); - break; - - case INS_GET_APP_CONFIGURATION: - handleGetAppConfiguration(tx); - break; - - case INS_KEEP_ALIVE: - handleKeepAlive(flags); - break; - - default: - THROW(0x6D00); - break; - } - } - CATCH(EXCEPTION_IO_RESET) { - THROW(EXCEPTION_IO_RESET); - } - CATCH_OTHER(e) { - switch (e & 0xF000) { - case 0x6000: - // Wipe the transaction context and report the exception - sw = e; - os_memset(&ctx.req.tx.content, 0, sizeof(ctx.req.tx.content)); - break; - case 0x9000: - // All is well - sw = e; - break; - default: - // Internal error - sw = 0x6800 | (e & 0x7FF); - break; - } - // Unexpected exception => report - G_io_apdu_buffer[*tx] = sw >> 8; - G_io_apdu_buffer[*tx + 1] = sw; - *tx += 2; - } - FINALLY { + if (p1 == P1_FIRST) { + // Note: we expect the first chunk to at least contain the bip path, flags and encoded message length completely + RETURN_ON_ERROR( + !read_bip32_path(&data_buffer, &data_length, ctx.req.msg.bip32Path, &ctx.req.msg.bip32PathLength) + || !read_u8(&data_buffer, &data_length, &ctx.req.msg.flags) + || !read_u32(&data_buffer, &data_length, &ctx.req.msg.messageLength), + SW_WRONG_DATA_LENGTH + ); + + ctx.req.msg.processedMessageLength = 0; + ctx.req.msg.isPrintableAscii = ctx.req.msg.messageLength <= MAX_PRINTABLE_MESSAGE_LENGTH; // ascii-check later + // Note that cx_sha256_init never throws and is not deprecated. See lcx_sha256.h and lcx_hash.h in Ledger sdk. + cx_sha256_init(&ctx.req.msg.prepare.messageHashContext); + cx_sha256_init(&ctx.req.msg.prepare.prefixedMessageHashContext); + + // Nimiq signed messages add a prefix to the message and then hash both together. + // This makes the calculated signature recognisable as a Nimiq specific signature and prevents signing arbitrary + // data (e.g. a transaction). This implementation is equivalent to the handling in Key.signMessage in Nimiq's + // Keyguard. + RETURN_ON_ERROR( + cx_hash_update( + /* hash context */ &ctx.req.msg.prepare.prefixedMessageHashContext.header, + /* data */ (uint8_t *) MESSAGE_SIGNING_PREFIX, + /* data length */ sizeof(MESSAGE_SIGNING_PREFIX) - /* exclude string terminator */ 1 + ), + SW_CRYPTOGRAPHY_FAIL, + "Failed to update message hash\n" + ); + // add data length printed as decimal number to the message prefix + char decimalMessageLength[STRING_LENGTH_UINT32]; + // note: not %lu (for unsigned long int) because int is already 32bit on ledgers (see "Memory Alignment" in + // Ledger docu), additionally Ledger's own implementation of sprintf does not support %lu (see os_printf.c) + snprintf(decimalMessageLength, sizeof(decimalMessageLength), "%u", ctx.req.msg.messageLength); + RETURN_ON_ERROR( + cx_hash_update( + /* hash context */ &ctx.req.msg.prepare.prefixedMessageHashContext.header, + /* data */ (uint8_t *) decimalMessageLength, + /* data length */ strlen(decimalMessageLength) + ), + SW_CRYPTOGRAPHY_FAIL, + "Failed to update message hash\n" + ); + } + + if (data_length != 0) { + RETURN_ON_ERROR( + (unsigned long)ctx.req.msg.processedMessageLength + data_length > ctx.req.msg.messageLength, + SW_WRONG_DATA_LENGTH, + "Message too long\n" + ); + // setup printable message + if (ctx.req.msg.messageLength <= MAX_PRINTABLE_MESSAGE_LENGTH) { + memmove(ctx.req.msg.printableMessage + ctx.req.msg.processedMessageLength, data_buffer, data_length); + ctx.req.msg.isPrintableAscii = ctx.req.msg.isPrintableAscii && is_printable_ascii(data_buffer, data_length); } + // hash message bytes + RETURN_ON_ERROR( + cx_hash_update( + /* has context */ &ctx.req.msg.prepare.messageHashContext.header, + /* data */ data_buffer, + /* data length */ data_length + ) + || cx_hash_update( + /* hash context */ &ctx.req.msg.prepare.prefixedMessageHashContext.header, + /* data */ data_buffer, + /* data length */ data_length + ), + SW_CRYPTOGRAPHY_FAIL, + "Failed to update message hash\n" + ); + ctx.req.msg.processedMessageLength += data_length; // guaranteed to not overflow due to the length check above + } + + if (p2 == P2_MORE) { + // Processing of current chunk finished; send success status word and let the caller continue with more chunks. + return SW_OK; + } + + // Create hashes and request user to sign + RETURN_ON_ERROR( + ctx.req.msg.processedMessageLength != ctx.req.msg.messageLength, + SW_WRONG_DATA_LENGTH, + "Invalid message length\n" + ); + // We're sharing the memory between the hash contexts and the created hashes to reduce memory usage. Make sure that + // we're not overwriting the hash contexts while generating the hashes (which is also safe with the current cx_hash + // implementation but could break anytime) by checking whether it fits the yet unused memory allocated in front of + // the output messageHash and prefixedMessageHash. Note that this is a compile time assertion. + _Static_assert( + sizeof(ctx.req.msg.prepare.messageHashContext) + sizeof(ctx.req.msg.prepare.prefixedMessageHashContext) + < sizeof(ctx.req.msg.confirm.printedMessageLabel) + sizeof(ctx.req.msg.confirm.printedMessage), + "Hash context memory overlaps with output hash memory\n" + ); +#if defined(NIMIQ_DEBUG) && NIMIQ_DEBUG + // Check output size manually, because cx_hash_final doesn't check it, in contrast to cx_hash_no_throw. + RETURN_ON_ERROR( + cx_hash_get_size(&ctx.req.msg.prepare.messageHashContext.header) + > sizeof(ctx.req.msg.confirm.messageHash) + || cx_hash_get_size(&ctx.req.msg.prepare.prefixedMessageHashContext.header) + > sizeof(ctx.req.msg.confirm.prefixedMessageHash), + SW_CRYPTOGRAPHY_FAIL, + "Invalid message hash output length\n" + ); +#endif + RETURN_ON_ERROR( + cx_hash_final( + /* hash context */ &ctx.req.msg.prepare.messageHashContext.header, + /* output */ ctx.req.msg.confirm.messageHash + ) + || cx_hash_final( + /* hash context */ &ctx.req.msg.prepare.prefixedMessageHashContext.header, + /* output */ ctx.req.msg.confirm.prefixedMessageHash + ), + SW_CRYPTOGRAPHY_FAIL, + "Failed to finalize message hash\n" + ); + + ui_message_signing( + // Depending on whether the data can be printed as ASCII or hex, default to ASCII, hex or hash display, unless + // a specific preference was provided. The user can still switch the display type during the confirmation (not + // implemented yet for NBGL). + ctx.req.msg.isPrintableAscii + && !(ctx.req.msg.flags & (MESSAGE_FLAG_PREFER_DISPLAY_TYPE_HEX | MESSAGE_FLAG_PREFER_DISPLAY_TYPE_HASH)) + ? MESSAGE_DISPLAY_TYPE_ASCII + : ctx.req.msg.messageLength <= MAX_PRINTABLE_MESSAGE_LENGTH + && !(ctx.req.msg.flags & MESSAGE_FLAG_PREFER_DISPLAY_TYPE_HASH) + ? MESSAGE_DISPLAY_TYPE_HEX + : MESSAGE_DISPLAY_TYPE_HASH, + false + ); + *out_start_async_reply = true; + return SW_OK; +} + +WARN_UNUSED_RESULT +sw_t handle_keep_alive(bool *out_start_async_reply) { + // Renew an async reply interrupted by u2f_send_keep_alive, which was then followed by a client request of + // INS_KEEP_ALIVE to continue the request, by starting a new async reply, which can eventually be resolved with the + // actual reply, or another keep alive timeout. + *out_start_async_reply = true; + return SW_OK; +} + +WARN_UNUSED_RESULT +sw_t handle_apdu(uint16_t *out_apdu_length, bool *out_start_async_reply) { + *out_apdu_length = 0; + *out_start_async_reply = false; + + // Don't need to check for os_global_pin_is_validated(), as in newer SDK versions it's already done in io_exchange. + + RETURN_ON_ERROR( + G_io_apdu_buffer[OFFSET_CLA] != CLA, + SW_CLA_NOT_SUPPORTED, + "Invalid CLA\n" + ); + + ctx.u2fTimer = U2F_REQUEST_TIMEOUT; + + switch (G_io_apdu_buffer[OFFSET_INS]) { + case INS_GET_PUBLIC_KEY: + PRINTF("Handle INS_GET_PUBLIC_KEY\n"); + return handle_get_public_key( + G_io_apdu_buffer[OFFSET_P1], + G_io_apdu_buffer[OFFSET_P2], + G_io_apdu_buffer + OFFSET_CDATA, + G_io_apdu_buffer[OFFSET_LC], + out_apdu_length, + out_start_async_reply + ); + case INS_SIGN_TX: + PRINTF("Handle INS_SIGN_TX\n"); + return handle_sign_transaction( + G_io_apdu_buffer[OFFSET_P1], + G_io_apdu_buffer[OFFSET_P2], + G_io_apdu_buffer + OFFSET_CDATA, + G_io_apdu_buffer[OFFSET_LC], + out_start_async_reply + ); + case INS_SIGN_MESSAGE: + PRINTF("Handle INS_SIGN_MESSAGE\n"); + return handle_sign_message( + G_io_apdu_buffer[OFFSET_P1], + G_io_apdu_buffer[OFFSET_P2], + G_io_apdu_buffer + OFFSET_CDATA, + G_io_apdu_buffer[OFFSET_LC], + out_start_async_reply + ); + case INS_KEEP_ALIVE: + PRINTF("Handle INS_KEEP_ALIVE\n"); + return handle_keep_alive(out_start_async_reply); + default: + RETURN_ERROR( + SW_INS_NOT_SUPPORTED, + "Invalid instruction\n" + ); } - END_TRY; } -void nimiq_main(void) { - volatile unsigned int rx = 0; - volatile unsigned int tx = 0; - volatile unsigned int flags = 0; +void nimiq_main() { + uint16_t command_apdu_length = 0; // length of a received APDU + uint16_t response_apdu_length = 0; // length of our response APDU + bool start_async_reply = false; // DESIGN NOTE: the bootloader ignores the way APDU are fetched. The only // goal is to retrieve APDU. // When APDU are to be fetched from multiple IOs, like NFC+USB+BLE, make // sure the io_event is called with a - // switch event, before the apdu is replied to the bootloader. This avoid + // switch event, before the APDU is replied to the bootloader. This avoid // APDU injection faults. for (;;) { - volatile unsigned short sw = 0; - - BEGIN_TRY { - TRY { - rx = tx; - tx = 0; // ensure no race in catch_other if io_exchange throws - // an error - rx = io_exchange(CHANNEL_APDU | flags, rx); - flags = 0; - - // no apdu received, well, reset the session, and reset the - // bootloader configuration - if (rx == 0) { - THROW(0x6982); - } - - PRINTF("New APDU received:\n%.*H\n", rx, G_io_apdu_buffer); + // Send current APDU response of length response_apdu_length from G_io_apdu_buffer, then read new APDU of length + // command_apdu_length into G_io_apdu_buffer. Sending of the response is skipped, if flag IO_ASYNCH_REPLY is + // set, in which case a reply can be delivered at a later time via io_finalize_async_reply, which sends it with + // flag IO_RETURN_AFTER_TX, which returns after the transfer and skips receiving a new APDU. I.e. new command + // APDUs are always received here, regardless of whether IO_ASYNCH_REPLY is set or not, as IO_RETURN_AFTER_TX is + // not set, and io_exchange will wait until a new APDU is received. + // Note that this method can THROW, which is then handled in main(). + uint8_t channel_and_flags = CHANNEL_APDU | (start_async_reply ? IO_ASYNCH_REPLY: 0); + command_apdu_length = io_exchange(channel_and_flags, response_apdu_length); + + sw_t sw; + if (command_apdu_length < OFFSET_LC + 1 + || command_apdu_length != G_io_apdu_buffer[OFFSET_LC] + OFFSET_CDATA) { + PRINTF("No or invalid length APDU received\n"); + sw = SW_WRONG_DATA_LENGTH; + } else { + PRINTF("New APDU received:\n%.*H\n", command_apdu_length, G_io_apdu_buffer); + sw = handle_apdu(&response_apdu_length, &start_async_reply); + } - handleApdu(&flags, &tx); - } - CATCH(EXCEPTION_IO_RESET) { - THROW(EXCEPTION_IO_RESET); - } - CATCH_OTHER(e) { - switch (e & 0xF000) { - case 0x6000: - // Wipe the transaction context and report the exception - sw = e; - os_memset(&ctx.req.tx.content, 0, sizeof(ctx.req.tx.content)); - break; - case 0x9000: - // All is well - sw = e; - break; - default: - // Internal error - sw = 0x6800 | (e & 0x7FF); - break; - } - // Unexpected exception => report - G_io_apdu_buffer[tx] = sw >> 8; - G_io_apdu_buffer[tx + 1] = sw; - tx += 2; - } - FINALLY { - } + if (sw != SW_OK) { + // Wipe global data to ensure it can't be continued to be used or misinterpreted. + memset(&ctx, 0, sizeof(ctx)); + // Enforce only sending an error code. + response_apdu_length = 0; + start_async_reply = false; } - END_TRY; - } - // return_to_dashboard: - return; + // Finalize the APDU response by appending the status word. The final response will be sent out with io_exchange + // in the next loop iteration. + G_io_apdu_buffer[response_apdu_length++] = (uint8_t) (sw >> 8); + G_io_apdu_buffer[response_apdu_length++] = (uint8_t) (sw); + } } -// override point, but nothing more to do -void io_seproxyhal_display(const bagl_element_t *element) { - io_seproxyhal_display_default((bagl_element_t *)element); -} +// Handler that io_exchange delegates to for channels other than CHANNEL_APDU, to handle data exchange via the hal +// (hardware abstaction layer), see os_io_seproxyhal.c in ledger-secure-sdk. +unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { + switch (channel & ~(IO_FLAGS)) { + case CHANNEL_KEYBOARD: + break; -void u2fSendKeepAlive() { - ctx.u2fTimer = 0; - G_io_apdu_buffer[0] = 0x6e; - G_io_apdu_buffer[1] = 0x02; - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + case CHANNEL_SPI: + // multiplexed io exchange over a SPI channel and TLV encapsulated protocol + if (tx_len) { + io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len); + + if (channel & IO_RESET_AFTER_REPLIED) { + reset(); + } + return 0; // nothing received from the master so far (it's a tx transaction) + } else { + return io_seproxyhal_spi_recv(G_io_apdu_buffer, sizeof(G_io_apdu_buffer), 0); + } + + default: + THROW(INVALID_PARAMETER); + } + return 0; } -unsigned char io_event(unsigned char channel) { - // nothing done with the event, throw an error on the transport layer if - // needed +unsigned char io_event(UNUSED_PARAMETER(uint8_t channel)) { + // nothing done with the event, throw an error on the transport layer if needed // can't have more than one tag in the reply, not supported yet. switch (G_io_seproxyhal_spi_buffer[0]) { - case SEPROXYHAL_TAG_FINGER_EVENT: - UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer); - break; - - case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT: - UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer); - break; - - case SEPROXYHAL_TAG_STATUS_EVENT: - if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID && - !(U4BE(G_io_seproxyhal_spi_buffer, 3) & - SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) { - THROW(EXCEPTION_IO_RESET); - } - // no break is intentional - default: - UX_DEFAULT_EVENT(); - break; - - case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT: - UX_DISPLAYED_EVENT({}); - break; - - case SEPROXYHAL_TAG_TICKER_EVENT: - -#ifndef TARGET_BLUE - if (G_io_apdu_media == IO_APDU_MEDIA_U2F && ctx.u2fTimer > 0) { - ctx.u2fTimer -= 100; - if (ctx.u2fTimer <= 0) { - u2fSendKeepAlive(); + case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT: + UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer); + break; + + case SEPROXYHAL_TAG_STATUS_EVENT: + if (G_io_app.apdu_media == IO_APDU_MEDIA_USB_HID && + !(U4BE(G_io_seproxyhal_spi_buffer, 3) & + SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) { + THROW(EXCEPTION_IO_RESET); } - } + // fallthrough is intentional + FALL_THROUGH; + + case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT: +#ifdef HAVE_BAGL + UX_DISPLAYED_EVENT({}); +#endif // HAVE_BAGL +#ifdef HAVE_NBGL + UX_DEFAULT_EVENT(); +#endif // HAVE_NBGL + break; + +#ifdef HAVE_NBGL + case SEPROXYHAL_TAG_FINGER_EVENT: + UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer); + break; +#endif // HAVE_NBGL - UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, { - if (UX_ALLOWED) { - if (ux_step_count) { - // prepare next screen - ux_step = (ux_step + 1) % ux_step_count; - // redisplay screen - UX_REDISPLAY(); + case SEPROXYHAL_TAG_TICKER_EVENT: + if (G_io_app.apdu_media == IO_APDU_MEDIA_U2F && ctx.u2fTimer > 0) { + ctx.u2fTimer -= 100; + if (ctx.u2fTimer == 0) { + u2f_send_keep_alive(); } } - }); -#endif - break; + UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, {}); + break; + + default: + UX_DEFAULT_EVENT(); + break; } // close the event if not done previously (by a display or whatever) @@ -2438,18 +890,11 @@ unsigned char io_event(unsigned char channel) { } void app_exit(void) { - BEGIN_TRY_L(exit) { - TRY_L(exit) { - os_sched_exit(-1); - } - FINALLY_L(exit) { - } - } - END_TRY_L(exit); + os_sched_exit(-1); } -__attribute__((section(".boot"))) int main(void) { +__attribute__((section(".boot"))) int main() { // exit critical section __asm volatile("cpsie i"); @@ -2457,54 +902,64 @@ __attribute__((section(".boot"))) int main(void) { os_boot(); for (;;) { - os_memset(&ctx.req.tx.content, 0, sizeof(ctx.req.tx.content)); + memset(&ctx, 0, sizeof(ctx)); UX_INIT(); BEGIN_TRY { TRY { io_seproxyhal_init(); -#ifdef HAVE_BLE - // grab the current plane mode setting - G_io_app.plane_mode = os_setting_get(OS_SETTING_PLANEMODE, NULL, 0); -#endif // HAVE_BLE - if (N_storage.initialized != 0x01) { - internalStorage_t storage; + internal_storage_t storage; storage.fidoTransport = 0x01; storage.initialized = 0x01; - nvm_write(&N_storage, (void *)&storage, - sizeof(internalStorage_t)); + nvm_write((void *) &N_storage, (void *) &storage, sizeof(internal_storage_t)); } // deactivate usb before activating USB_power(0); USB_power(1); - ui_idle(); + ui_menu_main(); #ifdef HAVE_BLE BLE_power(0, NULL); - BLE_power(1, "Nano X"); + BLE_power(1, NULL); #endif // HAVE_BLE -#if defined(TARGET_BLUE) - // setup the status bar colors (remembered after wards, even - // more if - // another app does not resetup after app switch) - UX_SET_STATUS_BAR_COLOR(0xFFFFFF, COLOR_APP); -#endif // #if defined(TARGET_BLUE) - nimiq_main(); } - CATCH(EXCEPTION_IO_RESET) { - // reset IO and UX - CLOSE_TRY; - continue; - } - CATCH_ALL { - CLOSE_TRY; - break; + CATCH_OTHER(e) { + // Note even though we're jumping out of the try block, we don't need to call CLOSE_TRY here, because + // CATCH_OTHER already does so automatically. + if (e == EXCEPTION_IO_RESET || e == INVALID_STATE) { + PRINTF("Received EXCEPTION_IO_RESET or INVALID_STATE. Resetting the app..."); + continue; // Reset IO and UX and restart. + } else { + // On other exceptions terminate the application. + PRINTF("Exiting app with exception 0x%04X\n", e); + // (Code below taken from SDK's lib_standard_app/main.c) +#ifdef HAVE_DEBUG_THROWS + // Disable USB and BLE, the app have crashed and is going to be exited + // This is necessary to avoid device freeze while displaying throw error + // in a specific case: + // - the app receives an APDU + // - the app throws before replying + // - the app displays the error on screen + // - the user unplug the NanoX instead of confirming the screen + // - the NanoX goes on battery power and display the lock screen + // - the user plug the NanoX instead of entering its pin + // - the device is frozen, battery should be removed + USB_power(0); +#ifdef HAVE_BLE + BLE_power(0, NULL); +#endif // HAVE_BLE + // Display crash info on screen for debug purpose + assert_display_exit(); +#else // HAVE_DEBUG_THROWS + break; +#endif // HAVE_DEBUG_THROWS + } } FINALLY { } diff --git a/src/nimiq_staking_utils.c b/src/nimiq_staking_utils.c new file mode 100644 index 0000000..cd52b25 --- /dev/null +++ b/src/nimiq_staking_utils.c @@ -0,0 +1,219 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#include + +#include "nimiq_staking_utils.h" +#include "nimiq_utils.h" +#include "signature_proof.h" + +// Staking transactions +// Incoming staking transactions are to the staking contract and encode their data in the recipient data, outgoing +// staking transaction are from the staking contract and encode their data in the sender data. Staking has been +// introduced / is only supported for Albatross, i.e. core-rs-albatross. For serialization format see: +// - IncomingStakingTransactionData in primitives/transaction/src/account/staking_contract/structs.rs for recipient +// - OutgoingStakingTransactionData in primitives/transaction/src/account/staking_contract/structs.rs for sender +// - rust types supported by serde: https://serde.rs/data-model.html#types +// - which are serialized to bytes in postcard format: https://postcard.jamesmunns.com/wire-format.html, see data() in +// transaction-builder/src/recipient/mod.rs and there used serialize_to_vec + +WARN_UNUSED_RESULT +error_t parse_staking_incoming_data(transaction_version_t version, uint8_t *data, uint16_t data_length, uint8_t *sender, + tx_data_staking_incoming_t *out) { + RETURN_ON_ERROR( + version == TRANSACTION_VERSION_LEGACY, + ERROR_INCORRECT_DATA, + "Staking is not supported for legacy transactions\n" + ); + + uint8_t validator_or_staker_address_buffer[20]; + // NULL means not specified, which is then the same as sender, as we create the staker signature proof with the + // sender account, if the empty signature proof was provided, see transaction signing in main.c + uint8_t *effective_validator_or_staker_address = NULL; + + _Static_assert( + sizeof(out->type) == 1, + "out->type has more than one byte. Need to take endianness into account when reading into a u8 pointer.\n" + ); + RETURN_ON_ERROR( + !read_u8(&data, &data_length, &out->type), + ERROR_READ + ); + switch (out->type) { + case CREATE_STAKER: + case UPDATE_STAKER: { + bool hasDelegation; + RETURN_ON_ERROR( + !read_bool(&data, &data_length, &hasDelegation), + ERROR_READ + ); + if (hasDelegation) { + uint8_t *delegation_address_pointer; + RETURN_ON_ERROR( + !read_sub_buffer(20, &data, &data_length, &delegation_address_pointer), + ERROR_READ + ); + RETURN_ON_ERROR( + print_address(delegation_address_pointer, out->create_staker_or_update_staker.delegation) + ); + } else { + COPY_FIXED_SIZE(out->create_staker_or_update_staker.delegation, ""); + } + if (out->type == UPDATE_STAKER) { + bool reactivate_all_stake; + RETURN_ON_ERROR( + !read_bool(&data, &data_length, &reactivate_all_stake), + ERROR_READ + ); + if (reactivate_all_stake) { + COPY_FIXED_SIZE(out->create_staker_or_update_staker.update_staker_reactivate_all_stake, "Yes"); + } else { + COPY_FIXED_SIZE(out->create_staker_or_update_staker.update_staker_reactivate_all_stake, "No"); + } + } + break; + } + + case ADD_STAKE: { + RETURN_ON_ERROR( + !read_sub_buffer(20, &data, &data_length, &effective_validator_or_staker_address), + ERROR_READ + ); + break; + } + + case SET_ACTIVE_STAKE: + case RETIRE_STAKE: { + uint64_t amount; + RETURN_ON_ERROR( + !read_u64(&data, &data_length, &amount), + ERROR_READ + ); + RETURN_ON_ERROR( + parse_amount(amount, "NIM", out->set_active_stake_or_retire_stake.amount) + ); + break; + } + + default: + // Note that validator transactions are not supported yet. + RETURN_ERROR( + ERROR_NOT_SUPPORTED, + "Invalid incoming staking transaction data type\n" + ); + } + + if (out->type != ADD_STAKE) { + // All types but ADD_STAKE encode a validator or staker signature proof at the end of the data. + out->has_validator_or_staker_signature_proof = true; + RETURN_ON_ERROR( + !read_signature_proof(&data, &data_length, &out->validator_or_staker_signature_proof), + ERROR_READ + ); + RETURN_ON_ERROR( + // Currently only ed25519 and empty merkle paths are supported. + out->validator_or_staker_signature_proof.type_and_flags != 0 + || out->validator_or_staker_signature_proof.merkle_path_length, + ERROR_NOT_SUPPORTED, + "Only ed25519 signature proofs without flags and merkle paths supported\n" + ); + if (!is_empty_default_signature_proof(out->validator_or_staker_signature_proof)) { + RETURN_ON_ERROR( + public_key_to_address(out->validator_or_staker_signature_proof.public_key, + validator_or_staker_address_buffer) + ); + effective_validator_or_staker_address = validator_or_staker_address_buffer; + } + } else { + out->has_validator_or_staker_signature_proof = false; + } + + RETURN_ON_ERROR( + data_length != 0, + ERROR_INVALID_LENGTH, + "Incoming staking data too long\n" + ); + + // Print the validator or staker address if it is different to the sender address. + // Other parts of the signature proofs don't need to be displayed or verified as they're verified by network nodes. + if (effective_validator_or_staker_address && memcmp(effective_validator_or_staker_address, sender, 20)) { + RETURN_ON_ERROR( + print_address(effective_validator_or_staker_address, out->validator_or_staker_address) + ); + } else { + // The staker address is the same as the sender address. Note that different to parse_htlc_creation_data or + // parse_vesting_creation_data we don't block non-basic sender types for staker creation here, because contract + // sender addresses would not be able to create a valid signature proof anyway as no signing key is known for + // the contract address. + COPY_FIXED_SIZE(out->validator_or_staker_address, ""); + } + + return ERROR_NONE; +} + +WARN_UNUSED_RESULT +error_t parse_staking_outgoing_data(transaction_version_t version, uint8_t *sender_data, uint16_t sender_data_length, + staking_outgoing_data_type_t *out_staking_outgoing_type) { + RETURN_ON_ERROR( + version == TRANSACTION_VERSION_LEGACY, + ERROR_INCORRECT_DATA, + "Staking is not supported for legacy transactions\n" + ); + + _Static_assert( + sizeof(*out_staking_outgoing_type) == 1, + "*out_staking_outgoing_type has more than one byte. Need to take endianness into account when reading into a " + "u8 pointer.\n" + ); + RETURN_ON_ERROR( + !read_u8(&sender_data, &sender_data_length, out_staking_outgoing_type), + ERROR_READ + ); + RETURN_ON_ERROR( + *out_staking_outgoing_type != DELETE_VALIDATOR && *out_staking_outgoing_type != REMOVE_STAKE, + ERROR_INCORRECT_DATA, + "Invalid outgoing staking type\n" + ); + + RETURN_ON_ERROR( + sender_data_length != 0, + ERROR_INVALID_LENGTH, + "Outgoing staking data too long\n" + ); + + return ERROR_NONE; +} + +bool is_staking_contract(uint8_t *address) { + // Staking contract address consists of 19 value 0 bytes followed by 1 value 1 byte, see STAKING_CONTRACT_ADDRESS in + // primitives/src/policy.rs in core-rs-albatross + for (uint8_t i = 0; i < 19; i++) { + if (address[i] != 0) return false; + } + return address[19] == 1; +} + +bool is_signaling_transaction_data(staking_incoming_data_type_t type) { + // See is_signaling in primitives/transaction/src/account/staking_contract/structs.rs in core-rs-albatross + return type == UPDATE_VALIDATOR + || type == DEACTIVATE_VALIDATOR + || type == REACTIVATE_VALIDATOR + || type == RETIRE_VALIDATOR + || type == UPDATE_STAKER + || type == SET_ACTIVE_STAKE + || type == RETIRE_STAKE; +} diff --git a/src/nimiq_staking_utils.h b/src/nimiq_staking_utils.h new file mode 100644 index 0000000..67ca87c --- /dev/null +++ b/src/nimiq_staking_utils.h @@ -0,0 +1,57 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifndef _NIMIQ_STAKING_UTILS_H_ +#define _NIMIQ_STAKING_UTILS_H_ + +#include +#include +#include "constants.h" +#include "error_macros.h" +#include "signature_proof.h" + +typedef struct { + staking_incoming_data_type_t type; + bool has_validator_or_staker_signature_proof; + signature_proof_t validator_or_staker_signature_proof; // only used if has_validator_or_staker_signature_proof set + // All data types have a validator or staker address. Empty string if equal to sender address. + char validator_or_staker_address[STRING_LENGTH_USER_FRIENDLY_ADDRESS]; + union { + // Note that validator transactions are not supported yet. + struct { + char delegation[STRING_LENGTH_USER_FRIENDLY_ADDRESS]; // Empty string if optional delegation address unset. + char update_staker_reactivate_all_stake[STRING_LENGTH_YES_NO]; // only used for UPDATE_STAKER; "Yes" or "No" + } create_staker_or_update_staker; + struct { + char amount[STRING_LENGTH_NIM_AMOUNT_WITH_TICKER]; + } set_active_stake_or_retire_stake; + }; +} tx_data_staking_incoming_t; + +WARN_UNUSED_RESULT +error_t parse_staking_incoming_data(transaction_version_t version, uint8_t *data, uint16_t data_length, uint8_t *sender, + tx_data_staking_incoming_t *out); + +WARN_UNUSED_RESULT +error_t parse_staking_outgoing_data(transaction_version_t version, uint8_t *sender_data, uint16_t sender_data_length, + staking_outgoing_data_type_t *out_staking_outgoing_type); + +bool is_staking_contract(uint8_t *address_bytes); + +bool is_signaling_transaction_data(staking_incoming_data_type_t type); + +#endif // _NIMIQ_STAKING_UTILS_H_ diff --git a/src/nimiq_utils.c b/src/nimiq_utils.c index f2fd004..56c8a4b 100644 --- a/src/nimiq_utils.c +++ b/src/nimiq_utils.c @@ -18,22 +18,15 @@ #include #include +// From Ledger SDK +#include "lcx_blake2.h" + #include "nimiq_utils.h" -#include "blake2b.h" +#include "nimiq_staking_utils.h" #include "base32.h" -#define MAX_SAFE_INTEGER 9007199254740991 - -static const char * captions[][5] = { - {"Basic Tx", NULL, NULL, NULL, NULL}, - {"Tx with Data", "Data", NULL, NULL, NULL}, - {"Cashlink Tx", NULL, NULL, NULL, NULL}, - {"Extended Tx", "Data", "Sender", "Sender Type", "Recipient Type"} // For future use, not yet supported -}; - -static const uint8_t AMOUNT_MAX_SIZE = 17; - -void iban_check(char in[32], char *check) { +WARN_UNUSED_RESULT +error_t iban_check(char base32[static 32], char *check) { unsigned int counter = 0; unsigned int offset = 0; unsigned int modulo = 0; @@ -45,7 +38,9 @@ void iban_check(char in[32], char *check) { char partial_number[10] = { 0 }; // According to IBAN standard, "NQ00" needs to be appended to the original address to calculate the checksum - strncpy(&address[0], &in[0], 32); + // Note: the input base32 is not required to be \0 terminated. We only look at the ascii chars before a potential + // string terminator here. Check that it's indeed valid base32 chars happens below. + memmove(address, base32, MIN(sizeof(address), 32)); address[32] = 'N'; address[33] = 'Q'; address[34] = '0'; @@ -53,28 +48,34 @@ void iban_check(char in[32], char *check) { // Convert the address to a number-only string for (unsigned int i = 0; i < 36; i++) { - if (70 <= counter) { - THROW(0x6700); // buffer overflow, signal error - } - if (48 <= address[i] && 57 >= address[i]) { + LEDGER_ASSERT( + // This assertion should hold, given the number of iterations and the fact that at least two address chars + // are digits, which increase the counter only by one. + counter < 70, + "Overflow in iban check" + ); + if (address[i] >= 48 && address[i] <= 57) { total_number[counter++] = address[i]; - } else if (65 <= address[i] && 90 >= address[i]) { + } else if (address[i] >= 65 && address[i] <= 90) { snprintf(&total_number[counter++], 3, "%d", address[i] - 55); // Letters convert to a two digit number, increase the counter one more time counter++; - } else if (97 <= address[i] && 122 >= address[i]) { + } else if (address[i] >= 97 && address[i] <= 122) { snprintf(&total_number[counter++], 3, "%d", address[i] - 87); // Letters convert to a two digit number, increase the counter one more time counter++; } else { - THROW(0x6a80); // invalid ascii code, signal error + RETURN_ERROR( + ERROR_INCORRECT_DATA, + "Invalid ascii code in iban check\n" + ); } } // Compute modulo-97 on the resulting number (do it in 32-bit pieces) counter = 0; for (unsigned int i = 0; i < 10; i++) { - strncpy(&partial_number[offset], &total_number[counter], 9 - offset); + memmove(&partial_number[offset], &total_number[counter], 9 - offset); counter += 9 - offset; for (unsigned int j = 0; j < 9; j++) { if (partial_number[j] != '\0') { @@ -91,14 +92,21 @@ void iban_check(char in[32], char *check) { } snprintf(check, 3, "%02d", 98 - modulo); + + return ERROR_NONE; } -void print_address(uint8_t *in, char *out) { +WARN_UNUSED_RESULT +error_t print_address(uint8_t *in, char *out) { unsigned int counter = 4; - char after_base32[32] = { 0 }; + char after_base32[33] = { 0 }; // includes one extra byte for the string terminator - base32_encode(in, 20, after_base32, 32); - iban_check(after_base32, &out[2]); + RETURN_ON_ERROR( + base32_encode(in, 20, after_base32, sizeof(after_base32)) + ); + RETURN_ON_ERROR( + iban_check(after_base32, &out[2]) + ); out[0] = 'N'; out[1] = 'Q'; @@ -112,27 +120,66 @@ void print_address(uint8_t *in, char *out) { // Make sure that the address string is always null-terminated out[44] = '\0'; + + return ERROR_NONE; } -void print_public_key(uint8_t *in, char *out) { - uint8_t after_blake[32] = { 0 }; - uint8_t short_blake[20] = { 0 }; +WARN_UNUSED_RESULT +error_t public_key_to_address(uint8_t *in, uint8_t *out) { + // See lcx_blake2.h and lcx_hash.h in Ledger sdk + unsigned char blake2b_hash[32]; + cx_blake2b_t blake2b_context; + RETURN_ON_ERROR( + cx_blake2b_init_no_throw(&blake2b_context, /* hash length in bits */ 256) + || cx_hash_no_throw(&blake2b_context.header, CX_LAST, in, 32, blake2b_hash, 32), + ERROR_CRYPTOGRAPHY + ); + memmove(out, blake2b_hash, 20); // the first 20 bytes of the hash are the Nimiq address + return ERROR_NONE; +} - blake2b(after_blake, 32, NULL, 0, in, 32); - memcpy(short_blake, after_blake, 20); +WARN_UNUSED_RESULT +error_t print_public_key_as_address(uint8_t *in, char *out) { + uint8_t address[20]; + RETURN_ON_ERROR( + public_key_to_address(in, address) + ); + RETURN_ON_ERROR( + print_address(address, out) + ); + return ERROR_NONE; +} - print_address(short_blake, out); +WARN_UNUSED_RESULT +error_t print_hex(uint8_t *data, uint16_t data_length, char *out, uint16_t out_length) { + RETURN_ON_ERROR( + // Check that it fits 2 hex chars per byte + string terminator. + out_length < data_length * 2 + 1, + ERROR_INVALID_LENGTH, + "Out buffer too small to fit hex\n" + ); + // not using Ledger's proprietary %.*H snprintf format, as it's non-standard + // (see https://github.com/LedgerHQ/app-bitcoin/pull/200/files) + for (uint16_t i = 0; i < data_length; i++) { + snprintf(out + i * 2, /* 2 hex chars + string terminator */ 3, "%02X", data[i]); + } + return ERROR_NONE; } -void print_amount(uint64_t amount, char *asset, char *out) { - char buffer[AMOUNT_MAX_SIZE]; +WARN_UNUSED_RESULT +error_t parse_amount(uint64_t amount, const char * const ticker, + char out[static STRING_LENGTH_NIM_AMOUNT_WITH_TICKER]) { + char buffer[STRING_LENGTH_NIM_AMOUNT]; // Note that we don't have to ensure that this is \0 terminated. uint64_t dVal = amount; - int i, j; + uint8_t i, j; // If the amount can't be represented safely in JavaScript, signal an error - if (MAX_SAFE_INTEGER < amount) THROW(0x6a80); + RETURN_ON_ERROR( + amount > MAX_SAFE_LUNA_AMOUNT, + ERROR_INCORRECT_DATA, + "Invalid amount\n" + ); - memset(buffer, 0, AMOUNT_MAX_SIZE); for (i = 0; dVal > 0 || i < 7; i++) { if (dVal > 0) { buffer[i] = (dVal % 10) + '0'; @@ -140,186 +187,846 @@ void print_amount(uint64_t amount, char *asset, char *out) { } else { buffer[i] = '0'; } - if (i == 4) { // satoshis to nim: 1 nim = 100 000 satoshis + if (i == 4) { // add decimal separator: 1 nim = 100 000 luna i += 1; buffer[i] = '.'; } - if (i >= AMOUNT_MAX_SIZE) { - THROW(0x6700); - } + LEDGER_ASSERT( + // This assertion should hold as we checked for MAX_SAFE_LUNA_AMOUNT + i < STRING_LENGTH_NIM_AMOUNT, + "Overflow in parse_amount" + ); } - // reverse order - for (i -= 1, j = 0; i >= 0 && j < AMOUNT_MAX_SIZE-1; i--, j++) { - out[j] = buffer[i]; + // Reverse order. Note: i is now the number of written characters. + for (j = 0; j < i; j++) { + out[j] = buffer[i - 1 - j]; } - // strip trailing 0s - for (j -= 1; j > 0; j--) { - if (out[j] != '0') break; + // Strip trailing 0s. Note: j starts with i as initial value. The loop stops the latest, when the decimal separator + // is reached. + for (; j > 0; j--) { + if (out[j - 1] != '0') break; } - j += 1; - // strip trailing . - if (out[j-1] == '.') j -= 1; + // strip trailing decimal separator + if (j > 0 && out[j - 1] == '.') { + j -= 1; + } - if (asset) { - // qualify amount + if (ticker) { + size_t ticker_length = strlen(ticker); + RETURN_ON_ERROR( + j + /* space */ 1 + ticker_length + /* string terminator */ 1 > STRING_LENGTH_NIM_AMOUNT_WITH_TICKER, + ERROR_INCORRECT_DATA, + "Result is longer than STRING_LENGTH_NIM_AMOUNT_WITH_TICKER\n" + ); + // Add ticker. out[j++] = ' '; - strcpy(out + j, asset); - out[j+strlen(asset)] = '\0'; + memcpy(out + j, ticker, ticker_length); + out[j + ticker_length] = '\0'; } else { out[j] = '\0'; } + return ERROR_NONE; } -void print_int(uint32_t id, char *out) { - char buffer[10]; - uint64_t dVal = id; - int i, j; - - memset(buffer, 0, 10); - for (i = 0; dVal > 0; i++) { - buffer[i] = (dVal % 10) + '0'; - dVal /= 10; - if (i >= 10) { - THROW(0x6700); - } +WARN_UNUSED_RESULT +error_t parse_network_id(transaction_version_t version, uint8_t network_id, + char out[static STRUCT_MEMBER_SIZE(parsed_tx_t, network)]) { + if (network_id == (version == TRANSACTION_VERSION_LEGACY ? 42 : 24)) { + memcpy(out, "Main", sizeof("Main")); + } else if (network_id == (version == TRANSACTION_VERSION_LEGACY ? 1 : 5)) { + memcpy(out, "Test", sizeof("Test")); + } else if (network_id == (version == TRANSACTION_VERSION_LEGACY ? 2 : 6)) { + memcpy(out, "Development", sizeof("Development")); + } else if (network_id == (version == TRANSACTION_VERSION_LEGACY ? 3 : 7)) { + memcpy(out, "Bounty", sizeof("Bounty")); + } else { + RETURN_ERROR( + ERROR_INCORRECT_DATA, + "Invalid network\n" + ); } - // reverse order - for (i -= 1, j = 0; i >= 0 && j < 10-1; i--, j++) { - out[j] = buffer[i]; + return ERROR_NONE; +} + +WARN_UNUSED_RESULT +error_t parse_normal_tx_data(uint8_t *data, uint16_t data_length, tx_data_normal_or_staking_outgoing_t *out, + bool *out_is_cashlink) { + // initiate with empty string / empty data + COPY_FIXED_SIZE(out->extra_data_label, ""); + COPY_FIXED_SIZE(out->extra_data, ""); + *out_is_cashlink = false; + + // Make sure we don't get called with more data than we can fit on the extra data field. + RETURN_ON_ERROR( + data_length > LENGTH_NORMAL_TX_DATA_MAX, + ERROR_INVALID_LENGTH, + "Extra data too long\n" + ); + + if (data == NULL || data_length == 0) { + return ERROR_NONE; } - if (j == 0) { - out[0] = '0'; - j++; + + // Check if it's the special Cashlink data which we do not want to display. + if (data_length == CASHLINK_MAGIC_NUMBER_LENGTH + && memcmp(data, CASHLINK_MAGIC_NUMBER, CASHLINK_MAGIC_NUMBER_LENGTH) == 0) { + *out_is_cashlink = true; + return ERROR_NONE; + } + // Check if there is any non-printable ASCII characters + if (!is_printable_ascii(data, data_length)) { + COPY_FIXED_SIZE(out->extra_data_label, "Data Hex"); + RETURN_ON_ERROR( + print_hex(data, data_length, out->extra_data, sizeof(out->extra_data)) + ); + return ERROR_NONE; } - out[j] = '\0'; + + // If there is not, copy the string to be displayed. Note that data is not a \0 terminated string, which is why we + // simply copy it with memmove and add a string terminator manually. + COPY_FIXED_SIZE(out->extra_data_label, "Data"); + memmove(out->extra_data, data, data_length); + out->extra_data[data_length] = '\0'; // Add string terminator. + + return ERROR_NONE; } -void print_network_id(uint8_t *in, char *out) { - if (42 == in[0]) { - strcpy(out, "Main"); - } else if (1 == in[0]) { - strcpy(out, "Test"); - } else if (2 == in[0]) { - strcpy(out, "Development"); - } else if (3 == in[0]) { - strcpy(out, "Bounty"); - } else { - THROW(0x6a80); +WARN_UNUSED_RESULT +error_t parse_htlc_creation_data(transaction_version_t version, uint8_t *data, uint16_t data_length, uint8_t *sender, + account_type_t sender_type, uint32_t validity_start_height, tx_data_htlc_creation_t *out) { + RETURN_ON_ERROR( + version != TRANSACTION_VERSION_LEGACY, + ERROR_NOT_SUPPORTED, + "HTLC creation not implemented yet for Albatross\n" + ); + + // Process refund address + uint8_t *refund_address_bytes; + RETURN_ON_ERROR( + !read_sub_buffer(20, &data, &data_length, &refund_address_bytes), + ERROR_READ + ); + RETURN_ON_ERROR( + print_address(refund_address_bytes, out->refund_address) + ); + out->is_refund_address_sender_address = memcmp(refund_address_bytes, sender, 20) == 0; + + RETURN_ON_ERROR( + // Although the refund address can be any address, specifying a contract as refund address is not recommendable + // because for the contract address there is no key that could create the required signature for the htlc refund + // proof. Protect the user from this scenario, as far as we can detect it. + out->is_refund_address_sender_address && sender_type != ACCOUNT_TYPE_BASIC, + ERROR_INCORRECT_DATA, + "HTLC refund address should not be a contract\n" + ); + + // Process redeem address + uint8_t *redeem_address_bytes; + RETURN_ON_ERROR( + !read_sub_buffer(20, &data, &data_length, &redeem_address_bytes), + ERROR_READ + ); + RETURN_ON_ERROR( + print_address(redeem_address_bytes, out->redeem_address) + ); + + // Process hash algorithm + hash_algorithm_t hash_algorithm; + _Static_assert( + sizeof(hash_algorithm) == 1, + "hash_algorithm has more than one byte. Need to take endianness into account when reading into a u8 pointer.\n" + ); + RETURN_ON_ERROR( + !read_u8(&data, &data_length, &hash_algorithm), + ERROR_READ + ); + switch (hash_algorithm) { + case HASH_ALGORITHM_BLAKE2B: + COPY_FIXED_SIZE(out->hash_algorithm, "BLAKE2b"); + break; + case HASH_ALGORITHM_SHA256: + COPY_FIXED_SIZE(out->hash_algorithm, "SHA-256"); + break; + case HASH_ALGORITHM_SHA512: + COPY_FIXED_SIZE(out->hash_algorithm, "SHA-512"); + break; + default: + // Invalid hash algorithm. Notably, ARGON2d is blacklisted for HTLCs. + RETURN_ERROR( + ERROR_INCORRECT_DATA, + "Invalid hash algorithm or blacklisted ARGON2d\n" + ); } + out->is_using_sha256 = hash_algorithm == HASH_ALGORITHM_SHA256; + + // Process hash root + uint8_t hash_size = hash_algorithm == HASH_ALGORITHM_SHA512 ? 64 : 32; + uint8_t *hash_bytes; + RETURN_ON_ERROR( + !read_sub_buffer(hash_size, &data, &data_length, &hash_bytes), + ERROR_READ + ); + // Print the hash as hex. + RETURN_ON_ERROR( + print_hex(hash_bytes, hash_size, out->hash_root, sizeof(out->hash_root)) + ); + + // Process hash count + uint8_t hash_count; + RETURN_ON_ERROR( + !read_u8(&data, &data_length, &hash_count), + ERROR_READ + ); + snprintf(out->hash_count, sizeof(out->hash_count), "%u", hash_count); + + // Process timeout + uint32_t timeout; + RETURN_ON_ERROR( + !read_u32(&data, &data_length, &timeout), + ERROR_READ + ); + // note: not %lu (for unsigned long int) because int is already 32bit on ledgers (see "Memory Alignment" in Ledger + // docu), additionally Ledger's own implementation of sprintf does not support %lu (see os_printf.c) + snprintf(out->timeout, sizeof(out->timeout), "%u", timeout); + out->is_timing_out_soon = timeout < validity_start_height + || timeout - validity_start_height < HTLC_TIMEOUT_SOON_THRESHOLD; + + RETURN_ON_ERROR( + data_length != 0, + ERROR_INVALID_LENGTH, + "Htlc data too long\n" + ); + + return ERROR_NONE; } -void print_extra_data(uint8_t *in, char *out, uint16_t data_size) { - // Extra safety check: make sure we don't get called with more data than - // we can fit on the extra data field. - if (MAX_DATA_LENGTH < data_size) THROW(0x6a80); +WARN_UNUSED_RESULT +error_t parse_vesting_creation_data(transaction_version_t version, uint8_t *data, uint16_t data_length, uint8_t *sender, + account_type_t sender_type, uint64_t tx_amount, tx_data_vesting_creation_t *out) { + RETURN_ON_ERROR( + version != TRANSACTION_VERSION_LEGACY, + ERROR_NOT_SUPPORTED, + "Vesting creation not implemented yet for Albatross\n" + ); - // Make sure that the string is always null-terminated - out[MAX_DATA_LENGTH] = '\0'; + // Note that this method could be quite heavy on the stack (depending on how well the compiler optimizes it). It + // could be refactored by allocating less variables by printing them directly or re-using variables, but at the cost + // of less readable code. - // Check if there is any non-printable ASCII characters - for (uint16_t i = 0; i < data_size; i++) { - if ((32 > in[i]) || (126 < in[i])) { - strcpy(out, "Binary data"); - return; + // Process owner address + uint8_t *owner_address_bytes; + RETURN_ON_ERROR( + !read_sub_buffer(20, &data, &data_length, &owner_address_bytes), + ERROR_READ + ); + RETURN_ON_ERROR( + print_address(owner_address_bytes, out->owner_address) + ); + out->is_owner_address_sender_address = memcmp(owner_address_bytes, sender, 20) == 0; + + RETURN_ON_ERROR( + // Although the owner address can be any address, specifying a contract as owner is not recommendable because + // for the contract address there is no key that could create the required signature for the vesting proof. + // Protect the user from this scenario, as far as we can detect it. + out->is_owner_address_sender_address && sender_type != ACCOUNT_TYPE_BASIC, + ERROR_INCORRECT_DATA, + "Vesting owner address should not be a contract\n" + ); + + // Read vesting parameters from data, depending on what is specified, and assign default values otherwise + uint32_t start_block = 0; + uint32_t step_block_count; + uint64_t step_amount = tx_amount; + uint64_t total_locked_amount = tx_amount; + if (data_length == 4) { + RETURN_ON_ERROR( + !read_u32(&data, &data_length, &step_block_count), + ERROR_READ + ); + } else { + RETURN_ON_ERROR( + !read_u32(&data, &data_length, &start_block) + || !read_u32(&data, &data_length, &step_block_count) + || !read_u64(&data, &data_length, &step_amount), + ERROR_READ + ); + + if (data_length == 8) { + RETURN_ON_ERROR( + !read_u64(&data, &data_length, &total_locked_amount), + ERROR_READ + ); } } - // If there is not, copy the string to be displayed - strncpy(out, (char *) in, data_size); + RETURN_ON_ERROR( + data_length != 0, + ERROR_INVALID_LENGTH, + "Vesting data too long\n" + ); + + // Translate into more user friendly information for display + uint32_t step_count; + uint32_t period; + uint32_t first_step_block_count; + uint64_t first_step_amount; + uint64_t last_step_amount; + uint64_t pre_vested_amount; + uint64_t helper_uint64; + if (!step_block_count || !step_amount) { + // Special case in vesting contracts where all funds are immediately vested and total_locked_amount is ignored. + // Set / overwrite all parameters accordingly. Also checked separately to avoid division by zero in other cases. + start_block = 0; + step_block_count = 0; + step_amount = tx_amount; + total_locked_amount = 0; + + step_count = 0; + period = 0; + first_step_block_count = 0; + first_step_amount = tx_amount; + last_step_amount = tx_amount; + pre_vested_amount = tx_amount; + } else { + // Normal case. Set so far undefined variables for user friendly information. + + // Actual vesting step count, potentially including steps that do not actually unlock real contract funds if + // total_locked_amount > tx_amount + helper_uint64 = (total_locked_amount / step_amount) + /* round up */ !!(total_locked_amount % step_amount); + RETURN_ON_ERROR( + // While this is theoretically possible in valid vesting contracts, for example for total_locked_amount == + // MAX_SAFE_LUNA_AMOUNT and step_amount == 1, this exceeds the currently supported number of blocks of the + // Nimiq blockchain and would lock funds for thousands to billions of years and is therefore a nonsense + // config that we want to protect users from. + // TODO re-evaluate this for Nimiq 2.0 + helper_uint64 > UINT32_MAX, + ERROR_INCORRECT_DATA, + "Vesting steps exceed number of possible Nimiq blocks\n" + ); + + // step_count + if (total_locked_amount <= tx_amount) { + // Each vesting step actually unlocks user funds. + step_count = (uint32_t) helper_uint64; + } else { + // The specified locked amount is higher than the actual contract amount, i.e. additional steps need + // to pass to vest the virtual, excess locked amount until the actual amount starts getting vested. + // Only count steps that actually unlock funds, subtracting steps that entirely only vest the virtual, + // excess locked amount. + // Note that (total_locked_amount - tx_amount) / step_amount is smaller than helper_uint64 and there is no + // risk of overflowing. + step_count = ((uint32_t) helper_uint64) - (total_locked_amount - tx_amount) / step_amount; + } + + // period + helper_uint64 = /* actual vesting step count */ helper_uint64 * step_block_count; + RETURN_ON_ERROR( + helper_uint64 + start_block > UINT32_MAX, + ERROR_INCORRECT_DATA, + "Vesting end exceeds number of possible Nimiq blocks\n" + ); + period = (uint32_t) helper_uint64; + + // remaining values for the standard total_locked_amount == tx_amount case + first_step_block_count = step_block_count; + first_step_amount = step_amount; + last_step_amount = total_locked_amount % step_amount ? total_locked_amount % step_amount : step_amount; + pre_vested_amount = 0; + + // adaptions for total_locked_amount != tx_amount + if (tx_amount > total_locked_amount) { + // Not actually the entire contract amount is locked, i.e. some amount is pre-vested. + pre_vested_amount = tx_amount - total_locked_amount; + } else if (tx_amount < total_locked_amount) { + // An additional, virtual, excess locked amount of total_locked_amount - tx_amount needs to be vested before + // the actual contract funds start getting vested. + // As we do not include steps that entirely only vest the locked excess amount in our step count and + // consider the step at which the first actual funds get unlocked as our first step, calculate the blocks + // that need to pass for the actual first funds to become available. + // This is guaranteed to not overflow as also the entire period was checked to fit an uint32. + first_step_block_count = step_block_count // blocks for the step that actually unlocks the first funds + + ((total_locked_amount - tx_amount) / step_amount) * step_block_count; // blocks for excess steps + // As the first step that unlocks actual funds can also be partly filled by the excess amount, calculate how + // much of the actual funds is actually unlocked. + first_step_amount = step_amount - ((total_locked_amount - tx_amount) % step_amount); + } + } + + // Print data + out->is_multi_step = step_count > 1; + // note: not %lu (for unsigned long int) because int is already 32bit on ledgers (see "Memory Alignment" in Ledger + // docu), additionally Ledger's own implementation of sprintf does not support %lu (see os_printf.c) + snprintf(out->start_block, sizeof(out->start_block), "%u", start_block); + snprintf(out->period, sizeof(out->period), "%u block%c", period, period != 1 ? 's' : '\0'); + snprintf(out->step_count, sizeof(out->step_count), "%u", step_count); + snprintf(out->step_block_count, sizeof(out->step_block_count), "%u block%c", step_block_count, + step_block_count != 1 ? 's' : '\0'); + snprintf(out->first_step_block_count, sizeof(out->first_step_block_count), "%u block%c", first_step_block_count, + first_step_block_count != 1 ? 's' : '\0'); + snprintf(out->first_step_block, sizeof(out->first_step_block), "%u", + start_block + first_step_block_count); // guaranteed to not overflow as also start_block + period <= UINT32_MAX + RETURN_ON_ERROR( + parse_amount(step_amount, "NIM", out->step_amount) + || parse_amount(first_step_amount, "NIM", out->first_step_amount) + || parse_amount(last_step_amount, "NIM", out->last_step_amount) + || parse_amount(pre_vested_amount, "NIM", out->pre_vested_amount), + ERROR_INCORRECT_DATA + ); + + return ERROR_NONE; } -void print_caption(uint8_t operationType, uint8_t captionType, char *out) { - char *in = ((char*) PIC(captions[operationType][captionType])); - if (in) { - strcpy(out, in); +// Buffer utils +// Note that these favor sanity checks and code readability over execution speed or low stack / memory usage. + +/** + * Reads the leading part of a buffer as pointer in the original buffer, and advances the buffer pointer by length of + * the extracted sub buffer. Notably, no copy of the sub buffer is created. + * @param sub_buffer_length - The length of the sub buffer to read. + * @param in_out_buffer - Buffer to read sub buffer from, after which the buffer pointer gets advanced by length read. + * @param in_out_buffer_length - Buffer length to check sub buffer length against. Afterwards reduced by length read. + * @param out_sub_buffer - A pointer to the sub buffer. This is a pointer in the original buffer (not to a copy) or NULL + * @return true on success, false on error + */ +WARN_UNUSED_RESULT +bool read_sub_buffer(uint16_t sub_buffer_length, uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, + uint8_t **out_sub_buffer) { + *out_sub_buffer = NULL; + if (sub_buffer_length == 0) { + return true; + } + if (in_out_buffer == NULL || *in_out_buffer == NULL || in_out_buffer_length == NULL) { + PRINTF("Buffer invalid\n"); + return false; + } + if (*in_out_buffer_length < sub_buffer_length) { + PRINTF("Buffer too short\n"); + return false; } + *out_sub_buffer = *in_out_buffer; + // Advance buffer and reduce the remaining buffer length. + *in_out_buffer += sub_buffer_length; + *in_out_buffer_length -= sub_buffer_length; + return true; } -uint16_t readUInt16Block(uint8_t *buffer) { - return buffer[1] + (buffer[0] << 8); +WARN_UNUSED_RESULT +bool read_u8(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint8_t *out_value) { + uint8_t *uint8_pointer; + RETURN_ON_ERROR( + !read_sub_buffer(1, in_out_buffer, in_out_buffer_length, &uint8_pointer), + false + ); + *out_value = *uint8_pointer; + return true; } -uint32_t readUInt32Block(uint8_t *buffer) { - return buffer[3] + (buffer[2] << 8) + (buffer[1] << 16) + (buffer[0] << 24); +WARN_UNUSED_RESULT +bool read_u16(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint16_t *out_value) { + uint8_t a, b; + RETURN_ON_ERROR( + !read_u8(in_out_buffer, in_out_buffer_length, &a) + || !read_u8(in_out_buffer, in_out_buffer_length, &b), + false + ); + *out_value = (((uint16_t) a) << 8) | b; + return true; } -uint64_t readUInt64Block(uint8_t *buffer) { - uint64_t i1 = buffer[3] + (buffer[2] << 8) + (buffer[1] << 16) + (buffer[0] << 24); - buffer += 4; - uint32_t i2 = buffer[3] + (buffer[2] << 8) + (buffer[1] << 16) + (buffer[0] << 24); - return i2 | (i1 << 32); +WARN_UNUSED_RESULT +bool read_u32(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint32_t *out_value) { + uint16_t a, b; + RETURN_ON_ERROR( + !read_u16(in_out_buffer, in_out_buffer_length, &a) + || !read_u16(in_out_buffer, in_out_buffer_length, &b), + false + ); + *out_value = (((uint32_t) a) << 16) | b; + return true; } -void parseTx(uint8_t *buffer, txContent_t *txContent) { - // Process the data length field - uint16_t data_length = readUInt16Block(buffer); - PRINTF("data length: %u\n", data_length); - buffer += 2; - - // Process the extra data field - if (0 == data_length) { - txContent->operationType = OPERATION_TYPE_BASIC_TX; - } else if ((CASHLINK_MAGIC_NUMBER_LENGTH == data_length) && - (0 == memcmp(buffer, CASHLINK_MAGIC_NUMBER, CASHLINK_MAGIC_NUMBER_LENGTH))) { - txContent->operationType = OPERATION_TYPE_CASHLINK_TX; - buffer += CASHLINK_MAGIC_NUMBER_LENGTH; - } else if (MAX_DATA_LENGTH >= data_length) { - txContent->operationType = OPERATION_TYPE_EXTRA_DATA_TX; - - print_extra_data(buffer, txContent->details1, data_length); - PRINTF("data: %s\n", txContent->details1); - buffer += data_length; - } else { - THROW(0x6a80); +WARN_UNUSED_RESULT +bool read_u64(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint64_t *out_value) { + uint32_t a, b; + RETURN_ON_ERROR( + !read_u32(in_out_buffer, in_out_buffer_length, &a) + || !read_u32(in_out_buffer, in_out_buffer_length, &b), + false + ); + *out_value = (((uint64_t) a) << 32) | b; + return true; +} + +WARN_UNUSED_RESULT +bool read_bool(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, bool *out_value) { + uint8_t uint8_value; + RETURN_ON_ERROR( + !read_u8(in_out_buffer, in_out_buffer_length, &uint8_value), + false + ); + *out_value = uint8_value != 0; + return true; +} + +/** + * Read a varint compatible with serde / postcard serialization, see https://postcard.jamesmunns.com/wire-format.html, + * also called LEB, see https://en.wikipedia.org/wiki/LEB128. Note that this serialization format is different from, for + * example, Bitcoin's variable length integers. + */ +WARN_UNUSED_RESULT +bool read_serde_uvarint(uint8_t max_bits, uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, + uint32_t *out_value) { + // This is currently an incomplete implementation, as it only supports reading single-byte varints, i.e. values up + // to 127, as we currently don't need reading higher values. Parameter max_bits is also mostly ignored yet in this + // implementation, we only check that it is not less that what's read from a single byte. + RETURN_ON_ERROR( + max_bits < 7, + false, + "Unsupported varint length\n" + ); + uint8_t byte; + RETURN_ON_ERROR( + !read_u8(in_out_buffer, in_out_buffer_length, &byte), + false + ); + uint8_t continuationBit = byte & 0x80; // the most significant bit is the continuation bit + RETURN_ON_ERROR( + continuationBit, + false, + "Unsupported multi-byte varint\n" + ); + *out_value = byte; + return true; +} + +/** + * Read a vector of bytes, compatible with serde / postcard serialization of a rust Vec. No copy of the data is + * created. + */ +WARN_UNUSED_RESULT +bool read_serde_vec_u8(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint8_t **out_data, + uint16_t *out_data_length) { + // A Vec is represented as a seq by serde, see https://serde.rs/data-model.html#types, which in turn is encoded as a + // varint(usize) followed by the u8 data by postcard, see https://postcard.jamesmunns.com/wire-format.html#23---seq. + // While usize is typically 32 bit or 64 bit, see https://postcard.jamesmunns.com/wire-format.html#isize-and-usize, + // depending on the host size, we limit it to 16 bit here, as the memory of Ledger devices can't hold that long data + // anyway. The u8 data is stored as individual bytes, see https://postcard.jamesmunns.com/wire-format.html#7---u8. + uint32_t data_length; // u32 as that's what read_serde_uvarint expects + RETURN_ON_ERROR( + !read_serde_uvarint(16, in_out_buffer, in_out_buffer_length, &data_length) + || !read_sub_buffer(data_length, in_out_buffer, in_out_buffer_length, out_data), + false + ); + *out_data_length = data_length; // no risk of overflow, as we limited the reading of the uvarint to 16 bits + return true; +} + +/** + * Read a bip32 path. The data is copied to the output. The output must have a size of at least MAX_BIP32_PATH_LENGTH. + */ +WARN_UNUSED_RESULT +bool read_bip32_path(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint32_t *out_bip32_path, + uint8_t *out_bip32_path_length) { + RETURN_ON_ERROR( + !read_u8(in_out_buffer, in_out_buffer_length, out_bip32_path_length), + false + ); + RETURN_ON_ERROR( + *out_bip32_path_length < 1 || *out_bip32_path_length > MAX_BIP32_PATH_LENGTH, + false, + "Invalid bip32 path length\n" + ); + for (uint8_t i = 0; i < *out_bip32_path_length; i++) { + RETURN_ON_ERROR( + !read_u32(in_out_buffer, in_out_buffer_length, &out_bip32_path[i]), + false + ); } + return true; +} - // Process the sender field - buffer += 20; // Ignore our own address for Basic Tx (even with data) - - // Process the sender account type field - uint8_t sender_type = buffer[0]; - buffer++; - if (0 != sender_type) THROW(0x6a80); // We only support basic accounts - - // Proccess the recipient field - print_address(buffer, txContent->recipient); - PRINTF("recipient: %s\n", txContent->recipient); - buffer += 20; - - // Proccess the recipient account type field - uint8_t recipient_type = buffer[0]; - buffer++; - if (0 != recipient_type) THROW(0x6a80); // We only support basic accounts - - // Proccess the value field - uint64_t value = readUInt64Block(buffer); - PRINTF("value: %lu\n", value); - print_amount(value, "NIM", txContent->value); - PRINTF("amount: %s\n", txContent->value); - buffer += 8; - - // Proccess the fee field - uint64_t fee = readUInt64Block(buffer); - PRINTF("fee: %lu\n", fee); - print_amount(fee, "NIM", txContent->fee); - PRINTF("fee amount: %s\n", txContent->fee); - buffer += 8; - - // Proccess the validity start field - uint32_t validity_start = readUInt32Block(buffer); - PRINTF("validity start: %u\n", validity_start); - print_int(validity_start, txContent->validity_start); - PRINTF("validity start string: %s\n", txContent->validity_start); - buffer += 4; - - // Proccess the validity start field - print_network_id(buffer, txContent->network); - buffer++; - - // Proccess the flags field - uint8_t flags = buffer[0]; +WARN_UNUSED_RESULT +error_t parse_tx(transaction_version_t version, uint8_t *buffer, uint16_t buffer_length, parsed_tx_t *out) { + RETURN_ON_ERROR( + version != TRANSACTION_VERSION_LEGACY && version != TRANSACTION_VERSION_ALBATROSS, + ERROR_NOT_SUPPORTED, + "Unsupported transaction version\n" + ); + + // For serialization format see serialize_content in primitives/transaction/src/lib.rs in core-rs-albatross. + + // Read the recipient data + uint16_t data_length; + RETURN_ON_ERROR( + !read_u16(&buffer, &buffer_length, &data_length), + ERROR_READ + ); + PRINTF("data length: %u\n", data_length); + uint8_t *data; + RETURN_ON_ERROR( + !read_sub_buffer(data_length, &buffer, &buffer_length, &data), + ERROR_READ + ); + + // Read the sender + uint8_t *sender, sender_type; + RETURN_ON_ERROR( + !read_sub_buffer(20, &buffer, &buffer_length, &sender) + || !read_u8(&buffer, &buffer_length, &sender_type), + ERROR_READ + ); + + // Read the recipient + uint8_t *recipient, recipient_type; + RETURN_ON_ERROR( + !read_sub_buffer(20, &buffer, &buffer_length, &recipient) + || !read_u8(&buffer, &buffer_length, &recipient_type), + ERROR_READ + ); + + // Process the value field + uint64_t value; + RETURN_ON_ERROR( + !read_u64(&buffer, &buffer_length, &value), + ERROR_READ + ); + PRINTF("value: %u\n", value); + RETURN_ON_ERROR( + parse_amount(value, "NIM", out->value) + ); + PRINTF("amount: %s\n", out->value); + + // Process the fee field + uint64_t fee; + RETURN_ON_ERROR( + !read_u64(&buffer, &buffer_length, &fee), + ERROR_READ + ); + PRINTF("fee: %u\n", fee); + RETURN_ON_ERROR( + parse_amount(fee, "NIM", out->fee) + ); + PRINTF("fee amount: %s\n", out->fee); + + // Read the validity start height + uint32_t validity_start_height; + RETURN_ON_ERROR( + !read_u32(&buffer, &buffer_length, &validity_start_height), + ERROR_READ + ); + + // Process the network field + uint8_t network_id; + RETURN_ON_ERROR( + !read_u8(&buffer, &buffer_length, &network_id), + ERROR_READ + ); + RETURN_ON_ERROR( + parse_network_id(version, network_id, out->network) + ); + + // Process the flags field + uint8_t flags; + RETURN_ON_ERROR( + !read_u8(&buffer, &buffer_length, &flags), + ERROR_READ + ); PRINTF("flags: %u\n", flags); - if (0 != flags) THROW(0x6a80); // No flags are supported yet + + // Read the sender data + uint16_t sender_data_length = 0; + uint8_t *sender_data = NULL; + if (version == TRANSACTION_VERSION_ALBATROSS) { + RETURN_ON_ERROR( + !read_serde_vec_u8(&buffer, &buffer_length, &sender_data, &sender_data_length), + ERROR_READ + ); + PRINTF("sender data length: %u\n", sender_data_length); + } + + RETURN_ON_ERROR( + buffer_length != 0, + ERROR_INVALID_LENGTH, + "Transaction too long\n" + ); + + // Note: the transaction validity checks here are mostly for good measure and not entirely thorough or strict as + // they don't need to be, because an invalid transaction will be rejected by the network nodes, even if we let it + // pass here in the app. + + if (sender_type == ACCOUNT_TYPE_STAKING) { + // Outgoing staking transaction from the staking contract. + RETURN_ON_ERROR( + // Would theoretically be allowed, but we don't support that yet. E.g. we don't support an unstaking tx to + // at the same time create a contract. It can also not be a transaction to the staking contract, because + // the sender and recipient address can not be the same. + flags || data_length, + ERROR_NOT_SUPPORTED, + "Invalid flags or recipient data\n" + ); + RETURN_ON_ERROR( + !is_staking_contract(sender), + ERROR_INCORRECT_DATA, + "Sender must be staking contract\n" + ); + + staking_outgoing_data_type_t staking_outgoing_type; + RETURN_ON_ERROR( + parse_staking_outgoing_data(version, sender_data, sender_data_length, &staking_outgoing_type) + ); + out->transaction_type = TRANSACTION_TYPE_STAKING_OUTGOING; + + switch (staking_outgoing_type) { + case REMOVE_STAKE: + out->transaction_label_type = TRANSACTION_LABEL_TYPE_STAKING_REMOVE_STAKE; + break; + default: + // Note that validator transactions are not supported yet. + RETURN_ERROR( + ERROR_NOT_SUPPORTED, + "Invalid outgoing staking transaction data type\n" + ); + } + + // Print the recipient address and set unused data to empty string. + RETURN_ON_ERROR( + print_address(recipient, out->type_specific.normal_or_staking_outgoing_tx.recipient) + ); + COPY_FIXED_SIZE(out->type_specific.normal_or_staking_outgoing_tx.extra_data_label, ""); + COPY_FIXED_SIZE(out->type_specific.normal_or_staking_outgoing_tx.extra_data, ""); + + return ERROR_NONE; + } + + switch (recipient_type) { + case ACCOUNT_TYPE_BASIC: { + RETURN_ON_ERROR( + // Signaling flag and sender data might theoretically be allowed, but we don't support that yet. + flags || sender_data_length, + ERROR_NOT_SUPPORTED, + "Invalid flags or sender data\n" + ); + + bool is_cashlink; + RETURN_ON_ERROR( + parse_normal_tx_data(data, data_length, &out->type_specific.normal_or_staking_outgoing_tx, &is_cashlink) + ); + PRINTF("data: %s - is Cashlink: %d\n", out->type_specific.normal_or_staking_outgoing_tx.extra_data, + is_cashlink); + + out->transaction_type = TRANSACTION_TYPE_NORMAL; + out->transaction_label_type = is_cashlink + ? TRANSACTION_LABEL_TYPE_CASHLINK + : TRANSACTION_LABEL_TYPE_REGULAR_TRANSACTION; + + // Print the recipient address + // We're ignoring the sender, as it's not too relevant where the funds are coming from. + RETURN_ON_ERROR( + print_address(recipient, out->type_specific.normal_or_staking_outgoing_tx.recipient) + ); + break; + } + + case ACCOUNT_TYPE_VESTING: + case ACCOUNT_TYPE_HTLC: { + RETURN_ON_ERROR( + // Contract creation flag must be set, and no other flags are allowed at the same time. + // Sender data would theoretically be allowed, but we don't currently support that. + flags != TX_FLAG_CONTRACT_CREATION || sender_data_length, + ERROR_NOT_SUPPORTED, + "Invalid flags or sender data\n" + ); + + // Note that we're ignoring the recipient address for contract creation transactions as it must be the + // deterministically calculated contract address, otherwise it's an invalid transaction rejected by the + // network nodes. + if (recipient_type == ACCOUNT_TYPE_VESTING) { + RETURN_ON_ERROR( + parse_vesting_creation_data(version, data, data_length, sender, sender_type, value, + &out->type_specific.vesting_creation_tx) + ); + out->transaction_type = TRANSACTION_TYPE_VESTING_CREATION; + out->transaction_label_type = TRANSACTION_LABEL_TYPE_VESTING_CREATION; + } else { // ACCOUNT_TYPE_HTLC + RETURN_ON_ERROR( + parse_htlc_creation_data(version, data, data_length, sender, sender_type, validity_start_height, + &out->type_specific.htlc_creation_tx) + ); + out->transaction_type = TRANSACTION_TYPE_HTLC_CREATION; + out->transaction_label_type = TRANSACTION_LABEL_TYPE_HTLC_CREATION; + } + break; + } + + case ACCOUNT_TYPE_STAKING: { + // Incoming staking transaction to the staking contract. + RETURN_ON_ERROR( + // Flags must be either unset or the signaling flag. + // Sender data would theoretically be allowed, but we don't currently support that. + (flags && flags != TX_FLAG_SIGNALING) || sender_data_length, + ERROR_NOT_SUPPORTED, + "Invalid flags or sender data\n" + ); + RETURN_ON_ERROR( + !is_staking_contract(recipient), + ERROR_INCORRECT_DATA, + "Recipient must be staking contract\n" + ); + + RETURN_ON_ERROR( + parse_staking_incoming_data(version, data, data_length, sender, + &out->type_specific.staking_incoming_tx) + ); + out->transaction_type = TRANSACTION_TYPE_STAKING_INCOMING; + + switch (out->type_specific.staking_incoming_tx.type) { + case CREATE_STAKER: + out->transaction_label_type = TRANSACTION_LABEL_TYPE_STAKING_CREATE_STAKER; + break; + case ADD_STAKE: + out->transaction_label_type = TRANSACTION_LABEL_TYPE_STAKING_ADD_STAKE; + break; + case UPDATE_STAKER: + out->transaction_label_type = TRANSACTION_LABEL_TYPE_STAKING_UPDATE_STAKER; + break; + case SET_ACTIVE_STAKE: + out->transaction_label_type = TRANSACTION_LABEL_TYPE_STAKING_SET_ACTIVE_STAKE; + break; + case RETIRE_STAKE: + out->transaction_label_type = TRANSACTION_LABEL_TYPE_STAKING_RETIRE_STAKE; + break; + default: + // Note that validator transactions are not supported yet. + RETURN_ERROR( + ERROR_NOT_SUPPORTED, + "Invalid incoming staking transaction data type\n" + ); + } + + RETURN_ON_ERROR( + (flags == TX_FLAG_SIGNALING) + != is_signaling_transaction_data(out->type_specific.staking_incoming_tx.type), + ERROR_INCORRECT_DATA, + "Signaling flag mismatch\n" + ); + break; + } + + default: + RETURN_ERROR( + ERROR_INCORRECT_DATA, + "Invalid recipient type\n" + ); + } + + return ERROR_NONE; +} + +bool is_printable_ascii(uint8_t *data, uint16_t data_length) { + for (uint16_t i = 0; i < data_length; i++) { + if ((data[i] < /* space */ 32) || (data[i] > /* tilde */ 126)) return false; + } + return true; } diff --git a/src/nimiq_utils.h b/src/nimiq_utils.h index dfb6abd..3f8b898 100644 --- a/src/nimiq_utils.h +++ b/src/nimiq_utils.h @@ -19,58 +19,158 @@ #define _NIMIQ_UTILS_H_ #include -#ifdef TEST -#include -#define THROW(code) { printf("error: %d", code); return; } -#define PRINTF(msg, arg) printf(msg, arg) -#define PIC(code) code -#define TARGET_NANOS 1 -#else -#include "os.h" -#endif // TEST - -#define MAX_DATA_LENGTH 64 -#define MAX_DATA_STRING_LENGTH (MAX_DATA_LENGTH + 1) // One more byte for the NULL string terminator - -#define CASHLINK_MAGIC_NUMBER "\x00\x82\x80\x92\x87" -#define CASHLINK_MAGIC_NUMBER_LENGTH 5 - -#define OPERATION_TYPE_BASIC_TX 0 -#define OPERATION_TYPE_EXTRA_DATA_TX 1 // Same as BASIC_TX but with Extra Data -#define OPERATION_TYPE_CASHLINK_TX 2 // BASIC_TX that funds a cashlink - -#define CAPTION_TYPE_OPERATION 0 -#define CAPTION_TYPE_DETAILS1 1 -#define CAPTION_TYPE_DETAILS2 2 -#define CAPTION_TYPE_DETAILS3 3 -#define CAPTION_TYPE_DETAILS4 4 - -typedef struct txContent_t { - char network[12]; - char recipient[45]; - char value[25]; - char fee[25]; - char validity_start[11]; - uint8_t operationType; - char details1[MAX_DATA_STRING_LENGTH]; - char details2[45]; - char details3[15]; - char details4[15]; -} txContent_t; - -void parseTx(uint8_t *buffer, txContent_t *txContent); - -void print_address(uint8_t *in, char *out); - -void print_public_key(uint8_t *in, char *out); - -void print_amount(uint64_t amount, char *asset, char *out); - -void print_network_id(uint8_t *in, char *out); - -void print_caption(uint8_t operationType, uint8_t captionType, char *out); - -void print_int(uint32_t in, char *out); - +#include + +#include "constants.h" +#include "utility_macros.h" +#include "error_macros.h" +#include "nimiq_staking_utils.h" + +#define LENGTH_NORMAL_TX_DATA_MAX 64 +// Ascii (1 char per byte + string terminator) or hex (2 char per byte + string terminator). +#define STRING_LENGTH_NORMAL_TX_DATA_MAX (LENGTH_NORMAL_TX_DATA_MAX * 2 + 1) + +// TODO The threshold for short timeouts should be re-evaluated for Nimiq 2.0. However, keeping the current threshold is +// no security risk at the time of switching to 2.0, as the threshold would be rather needed to be increased than +// reduced, thus ui steps skipped for short timeouts will be displayed even though we could skip them. +#define HTLC_TIMEOUT_SOON_THRESHOLD (60 * 24 * 31 * 2); // ~ 2 months at 1 minute block time + +// Data printed for display. +// Note that this does not include any information about where the funds are coming from (a regular account, htlc, +// vesting contract, which address, ...) as this is not too relevant for the user and also not displayed by other apps +// like the Bitcoin app. +// Also note that while pre-processing all the data is nice, it's a bit wasteful in allocated memory usage. If we'd run +// into issues with low memory, we should switch to printing data on demand in the flow ux steps' init methods. + +typedef struct { + char recipient[STRING_LENGTH_USER_FRIENDLY_ADDRESS]; + char extra_data_label[MAX(sizeof("Data"), sizeof("Data Hex"))]; + char extra_data[STRING_LENGTH_NORMAL_TX_DATA_MAX]; +} tx_data_normal_or_staking_outgoing_t; + +typedef struct { + bool is_refund_address_sender_address; + bool is_timing_out_soon; + bool is_using_sha256; + char redeem_address[STRING_LENGTH_USER_FRIENDLY_ADDRESS]; + char refund_address[STRING_LENGTH_USER_FRIENDLY_ADDRESS]; + char hash_root[64 * 2 + 1]; // hash root can be up to 64 bytes; display as hex + string terminator needs 129 chars + char hash_algorithm[MAX(sizeof("BLAKE2b"), MAX(sizeof("SHA-256"), sizeof("SHA-512")))]; + char hash_count[STRING_LENGTH_UINT8]; + char timeout[STRING_LENGTH_UINT32]; +} tx_data_htlc_creation_t; + +typedef struct { + bool is_owner_address_sender_address; + bool is_multi_step; + char owner_address[STRING_LENGTH_USER_FRIENDLY_ADDRESS]; + char start_block[STRING_LENGTH_UINT32]; + char period[STRING_LENGTH_WITH_SUFFIX(STRING_LENGTH_UINT32, " blocks")]; + char step_count[STRING_LENGTH_UINT32]; + char step_block_count[STRING_LENGTH_WITH_SUFFIX(STRING_LENGTH_UINT32, " blocks")]; + // Note: first_step_block_count, first_step_block, first_step_amount and pre_vested_amount are not all needed at the + // same time and could therefore be moved into a union to save some memory. As however tx_data_htlc_creation_t in + // the parsed_tx_t.type_specific union is bigger anyways, we currently don't have to do this optimization. + char first_step_block_count[STRING_LENGTH_WITH_SUFFIX(STRING_LENGTH_UINT32, " blocks")]; + char first_step_block[STRING_LENGTH_UINT32]; + char step_amount[STRING_LENGTH_NIM_AMOUNT_WITH_TICKER]; + char first_step_amount[STRING_LENGTH_NIM_AMOUNT_WITH_TICKER]; + char last_step_amount[STRING_LENGTH_NIM_AMOUNT_WITH_TICKER]; + char pre_vested_amount[STRING_LENGTH_NIM_AMOUNT_WITH_TICKER]; +} tx_data_vesting_creation_t; + +typedef struct { + union { + tx_data_normal_or_staking_outgoing_t normal_or_staking_outgoing_tx; + tx_data_htlc_creation_t htlc_creation_tx; + tx_data_vesting_creation_t vesting_creation_tx; + tx_data_staking_incoming_t staking_incoming_tx; + } type_specific; + + transaction_type_t transaction_type; + transaction_label_type_t transaction_label_type; +#ifdef HAVE_BAGL + char transaction_label[MAX( + sizeof("Transaction"), MAX( + sizeof("Cashlink"), MAX( + sizeof("Vesting"), MAX( + sizeof("HTLC / Swap"), MAX( + sizeof("Create Staker"), MAX( + sizeof("Add Stake"), MAX( + sizeof("Update Staker"), MAX( + sizeof("Set Active Stake"), MAX( + sizeof("Retire Stake"), + sizeof("Unstake"))))))))) + )]; +#endif + char value[STRING_LENGTH_NIM_AMOUNT_WITH_TICKER]; + char fee[STRING_LENGTH_NIM_AMOUNT_WITH_TICKER]; + char network[MAX(sizeof("Main"), MAX(sizeof("Test"), MAX(sizeof("Development"), sizeof("Bounty"))))]; +} parsed_tx_t; + +WARN_UNUSED_RESULT +error_t parse_tx(transaction_version_t version, uint8_t *buffer, uint16_t buffer_length, parsed_tx_t *out); + +WARN_UNUSED_RESULT +error_t print_address(uint8_t *in, char *out); + +WARN_UNUSED_RESULT +error_t public_key_to_address(uint8_t *in, uint8_t *out); + +WARN_UNUSED_RESULT +error_t print_public_key_as_address(uint8_t *in, char *out); + +WARN_UNUSED_RESULT +error_t print_hex(uint8_t *data, uint16_t data_length, char *out, uint16_t out_length); + +WARN_UNUSED_RESULT +error_t parse_amount(uint64_t amount, const char * const ticker, char out[static STRING_LENGTH_NIM_AMOUNT_WITH_TICKER]); + +WARN_UNUSED_RESULT +error_t parse_network_id(transaction_version_t version, uint8_t network_id, char *out); + +WARN_UNUSED_RESULT +error_t parse_normal_tx_data(uint8_t *data, uint16_t data_length, tx_data_normal_or_staking_outgoing_t *out, + bool *out_is_cashlink); + +WARN_UNUSED_RESULT +error_t parse_htlc_creation_data(transaction_version_t version, uint8_t *data, uint16_t data_length, uint8_t *sender, + account_type_t sender_type, uint32_t validity_start_height, tx_data_htlc_creation_t *out); + +WARN_UNUSED_RESULT +error_t parse_vesting_creation_data(transaction_version_t version, uint8_t *data, uint16_t data_length, uint8_t *sender, + account_type_t sender_type, uint64_t tx_amount, tx_data_vesting_creation_t *out); + +WARN_UNUSED_RESULT +bool read_sub_buffer(uint16_t sub_buffer_length, uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, + uint8_t **out_sub_buffer); + +WARN_UNUSED_RESULT +bool read_u8(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint8_t *out_value); + +WARN_UNUSED_RESULT +bool read_u16(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint16_t *out_value); + +WARN_UNUSED_RESULT +bool read_u32(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint32_t *out_value); + +WARN_UNUSED_RESULT +bool read_u64(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint64_t *out_value); + +WARN_UNUSED_RESULT +bool read_bool(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, bool *out_value); + +WARN_UNUSED_RESULT +bool read_serde_uvarint(uint8_t max_bits, uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint32_t *out_value); + +WARN_UNUSED_RESULT +bool read_serde_vec_u8(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint8_t **out_data, + uint16_t *out_data_length); + +WARN_UNUSED_RESULT +bool read_bip32_path(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, uint32_t *out_bip32_path, + uint8_t *out_bip32_path_length); + +bool is_printable_ascii(uint8_t *data, uint16_t data_length); #endif // _NIMIQ_UTILS_H_ diff --git a/src/nimiq_ux.h b/src/nimiq_ux.h new file mode 100644 index 0000000..9ed53c5 --- /dev/null +++ b/src/nimiq_ux.h @@ -0,0 +1,33 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifndef _NIMIQ_UX_H_ +#define _NIMIQ_UX_H_ + +#include + +#include "constants.h" + +void ui_menu_main(); + +void ui_public_key(); + +void ui_transaction_signing(); + +void ui_message_signing(message_display_type_t messageDisplayType, bool startAtMessageDisplay); + +#endif // _NIMIQ_UX_H_ diff --git a/src/nimiq_ux_bagl.c b/src/nimiq_ux_bagl.c new file mode 100644 index 0000000..1aebb7c --- /dev/null +++ b/src/nimiq_ux_bagl.c @@ -0,0 +1,619 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifdef HAVE_BAGL + +// From Ledger SDK +#include "ux.h" + +#include "nimiq_ux.h" +#include "globals.h" +#include "nimiq_ux_bagl_macros.h" +#include "nimiq_ux_utils_transaction_signing.h" +#include "nimiq_ux_utils_message_signing.h" + +// These are declared in main.c +void on_rejected(); +void on_address_approved(); +void on_transaction_approved(); +void on_message_approved(); +void app_exit(); + +// Main menu UI steps and flow + +UX_STEP_NOCB( + ux_idle_flow_welcome_step, + nn, + { + "Application", + "is ready", + }); +UX_STEP_NOCB( + ux_idle_flow_version_step, + bn, + { + "Version", + APPVERSION, + }); +UX_STEP_CB( + ux_idle_flow_quit_step, + pb, + app_exit(), + { + &C_icon_dashboard, + "Quit", + }); + +UX_FLOW(ux_idle_flow, + &ux_idle_flow_welcome_step, + &ux_idle_flow_version_step, + &ux_idle_flow_quit_step, + FLOW_LOOP +); + +////////////////////////////////////////////////////////////////////// + +// Address confirmation UI steps and flow + +UX_STEP_NOCB( + ux_public_key_flow_address_step, + paging, + { + "Address", + ctx.req.pk.address, + }); +UX_STEP_CB( + ux_public_key_flow_approve_step, + pb, + { + on_address_approved(); + ui_menu_main(); + }, + { + &C_icon_validate_14, + "Approve", + }); +UX_STEP_CB( + ux_public_key_flow_reject_step, + pb, + { + on_rejected(); + ui_menu_main(); + }, + { + &C_icon_crossmark, + "Reject", + }); + +UX_FLOW(ux_public_key_flow, + &ux_public_key_flow_address_step, + &ux_public_key_flow_approve_step, + &ux_public_key_flow_reject_step +); + +////////////////////////////////////////////////////////////////////// + +// Generic transaction confirmation UI steps + +UX_STEP_NOCB( + ux_transaction_generic_flow_transaction_type_step, + pnn, + { + &C_icon_eye, + "Confirm", + PARSED_TX.transaction_label, + }); +UX_OPTIONAL_STEP_NOCB( + ux_transaction_generic_flow_amount_step, + paging, + ux_transaction_generic_has_amount_entry(), // amount can be 0 for signaling transactions + { + "Amount", + PARSED_TX.value, + }); +UX_OPTIONAL_STEP_NOCB( + ux_transaction_generic_flow_fee_step, + paging, + ux_transaction_generic_has_fee_entry(), + { + "Fee", + PARSED_TX.fee, + }); +UX_STEP_NOCB( + ux_transaction_generic_flow_network_step, + paging, + { + "Network", + PARSED_TX.network, + }); +UX_STEP_CB( + ux_transaction_generic_flow_approve_step, + pbb, + { + on_transaction_approved(); + ui_menu_main(); + }, + { + &C_icon_validate_14, + "Accept", + "and send", + }); +UX_STEP_CB( + ux_transaction_generic_flow_reject_step, + pb, + { + on_rejected(); + ui_menu_main(); + }, + { + &C_icon_crossmark, + "Reject", + }); + +////////////////////////////////////////////////////////////////////// + +// Normal, non contract creation transaction specific UI steps and flow + +UX_STEP_NOCB( + ux_transaction_normal_or_staking_outgoing_flow_recipient_step, + paging, + { + "Recipient", + PARSED_TX_NORMAL_OR_STAKING_OUTGOING.recipient, + }); +UX_OPTIONAL_STEP_NOCB( + ux_transaction_normal_or_staking_outgoing_flow_data_step, + paging, + ux_transaction_normal_or_staking_outgoing_has_data_entry(), + { + PARSED_TX_NORMAL_OR_STAKING_OUTGOING.extra_data_label, + PARSED_TX_NORMAL_OR_STAKING_OUTGOING.extra_data, + }); + +UX_FLOW(ux_transaction_normal_or_staking_outgoing_flow, + &ux_transaction_generic_flow_transaction_type_step, + &ux_transaction_generic_flow_amount_step, // optional, but always displayed as this is not a signaling transaction + &ux_transaction_normal_or_staking_outgoing_flow_recipient_step, + &ux_transaction_normal_or_staking_outgoing_flow_data_step, // optional + &ux_transaction_generic_flow_fee_step, // optional + &ux_transaction_generic_flow_network_step, + &ux_transaction_generic_flow_approve_step, + &ux_transaction_generic_flow_reject_step +); + +////////////////////////////////////////////////////////////////////// + +// HTLC creation specific UI steps and flow + +UX_STEP_NOCB( + ux_htlc_creation_flow_redeem_address_step, + paging, + { + "HTLC Recipient", + PARSED_TX_HTLC_CREATION.redeem_address, + }); +UX_OPTIONAL_STEP_NOCB( + ux_htlc_creation_flow_refund_address_step, + paging, + ux_transaction_htlc_creation_has_refund_address_entry(), + { + "Refund to", + PARSED_TX_HTLC_CREATION.refund_address, + }); +UX_STEP_NOCB( + ux_htlc_creation_flow_hash_root_step, + paging, + { + "Hashed Secret", // more user friendly label for hash root + PARSED_TX_HTLC_CREATION.hash_root, + }); +UX_OPTIONAL_STEP_NOCB( + ux_htlc_creation_flow_hash_algorithm_step, + paging, + ux_transaction_htlc_creation_has_hash_algorithm_entry(), + { + "Hash Algorithm", + PARSED_TX_HTLC_CREATION.hash_algorithm, + }); +UX_OPTIONAL_STEP_NOCB( + ux_htlc_creation_flow_hash_count_step, + paging, + ux_transaction_htlc_creation_has_hash_count_entry(), + { + "Hash Steps", // more user friendly label for hash count + PARSED_TX_HTLC_CREATION.hash_count, + }); +UX_OPTIONAL_STEP_NOCB( + ux_htlc_creation_flow_timeout_step, + paging, + ux_transaction_htlc_creation_has_timeout_entry(), + { + "HTLC Expiry Block", // more user friendly label for timeout + PARSED_TX_HTLC_CREATION.timeout, + }); + +UX_FLOW(ux_transaction_htlc_creation_flow, + &ux_transaction_generic_flow_transaction_type_step, + &ux_transaction_generic_flow_amount_step, // optional, but always displayed as this is not a signaling transaction + &ux_htlc_creation_flow_redeem_address_step, + &ux_htlc_creation_flow_refund_address_step, // optional + &ux_htlc_creation_flow_hash_root_step, + &ux_htlc_creation_flow_hash_algorithm_step, // optional + &ux_htlc_creation_flow_hash_count_step, // optional + &ux_htlc_creation_flow_timeout_step, // optional + &ux_transaction_generic_flow_fee_step, // optional + &ux_transaction_generic_flow_network_step, + &ux_transaction_generic_flow_approve_step, + &ux_transaction_generic_flow_reject_step +); + +////////////////////////////////////////////////////////////////////// + +// Vesting Contract Creation specific UI steps and flow + +UX_OPTIONAL_STEP_NOCB( + ux_vesting_creation_flow_owner_address_step, + paging, + ux_transaction_vesting_creation_has_owner_address_entry(), + { + "Vesting Owner", + PARSED_TX_VESTING_CREATION.owner_address, + }); +UX_OPTIONAL_STEP_NOCB( + ux_vesting_creation_flow_single_vesting_block_step, // simplified ui for step_count == 1 case + paging, + ux_transaction_vesting_creation_has_single_vesting_block_entry(), + { + "Vested at Block", + PARSED_TX_VESTING_CREATION.first_step_block, + }); +UX_OPTIONAL_STEP_NOCB( + ux_vesting_creation_flow_start_block_step, + paging, + ux_transaction_vesting_creation_has_start_and_period_and_step_count_and_step_duration_entries(), + { + "Vesting Start Block", + PARSED_TX_VESTING_CREATION.start_block, + }); +UX_OPTIONAL_STEP_NOCB( + ux_vesting_creation_flow_period_step, + paging, + ux_transaction_vesting_creation_has_start_and_period_and_step_count_and_step_duration_entries(), + { + "Vesting Period", + PARSED_TX_VESTING_CREATION.period, + }); +UX_OPTIONAL_STEP_NOCB( + ux_vesting_creation_flow_step_count_step, + paging, + ux_transaction_vesting_creation_has_start_and_period_and_step_count_and_step_duration_entries(), + { + "Vesting Steps", + PARSED_TX_VESTING_CREATION.step_count, + }); +UX_OPTIONAL_STEP_NOCB( + ux_vesting_creation_flow_step_block_count_step, + paging, + ux_transaction_vesting_creation_has_start_and_period_and_step_count_and_step_duration_entries(), + { + "Blocks Per Step", + PARSED_TX_VESTING_CREATION.step_block_count, + }); +UX_OPTIONAL_STEP_NOCB( + ux_vesting_creation_flow_first_step_block_count_step, + paging, + ux_transaction_vesting_creation_has_first_step_duration_entry(), + { + "First Step", + PARSED_TX_VESTING_CREATION.first_step_block_count, + }); +UX_OPTIONAL_STEP_NOCB( + ux_vesting_creation_flow_step_amount_step, + paging, + ux_transaction_vesting_creation_has_step_amount_entry(), + { + "Vested per Step", + PARSED_TX_VESTING_CREATION.step_amount, + }); +UX_OPTIONAL_STEP_NOCB( + ux_vesting_creation_flow_first_step_amount_step, + paging, + ux_transaction_vesting_creation_has_first_step_amount_entry(), + { + "First Step", + PARSED_TX_VESTING_CREATION.first_step_amount, + }); +UX_OPTIONAL_STEP_NOCB( + ux_vesting_creation_flow_last_step_amount_step, + paging, + ux_transaction_vesting_creation_has_last_step_amount_entry(), + { + "Last Step", + PARSED_TX_VESTING_CREATION.last_step_amount, + }); +UX_OPTIONAL_STEP_NOCB( + ux_vesting_creation_flow_pre_vested_amount_step, + paging, + ux_transaction_vesting_creation_has_pre_vested_amount_entry(), + { + "Pre-Vested", + PARSED_TX_VESTING_CREATION.pre_vested_amount, + }); + +UX_FLOW(ux_transaction_vesting_creation_flow, + &ux_transaction_generic_flow_transaction_type_step, + &ux_transaction_generic_flow_amount_step, // optional, but always displayed as this is not a signaling transaction + &ux_vesting_creation_flow_owner_address_step, // optional + &ux_vesting_creation_flow_single_vesting_block_step, // optional + &ux_vesting_creation_flow_start_block_step, // optional + &ux_vesting_creation_flow_period_step, // optional + &ux_vesting_creation_flow_step_count_step, // optional + &ux_vesting_creation_flow_step_block_count_step, // optional + &ux_vesting_creation_flow_first_step_block_count_step, // optional + &ux_vesting_creation_flow_step_amount_step, // optional + &ux_vesting_creation_flow_first_step_amount_step, // optional + &ux_vesting_creation_flow_last_step_amount_step, // optional + &ux_vesting_creation_flow_pre_vested_amount_step, // optional + &ux_transaction_generic_flow_fee_step, // optional + &ux_transaction_generic_flow_network_step, + &ux_transaction_generic_flow_approve_step, + &ux_transaction_generic_flow_reject_step +); + +////////////////////////////////////////////////////////////////////// + +// Incoming staking transaction (transactions to the staking contract) specific UI steps and flow + +UX_OPTIONAL_STEP_NOCB( + ux_staking_incoming_flow_set_active_stake_or_retire_stake_amount_step, + paging, + ux_transaction_staking_incoming_has_set_active_stake_or_retire_stake_amount_entry(), + { + "Amount", + PARSED_TX_STAKING_INCOMING.set_active_stake_or_retire_stake.amount, + }); +UX_OPTIONAL_STEP_NOCB( + ux_staking_incoming_flow_staker_address_step, + paging, + ux_transaction_staking_incoming_has_staker_address_entry(), + { + "Staker", + PARSED_TX_STAKING_INCOMING.validator_or_staker_address, + }); +UX_OPTIONAL_STEP_NOCB( + ux_staking_incoming_flow_create_staker_or_update_staker_delegation_step, + paging, + ux_transaction_staking_incoming_has_create_staker_or_update_staker_delegation_entry(), + { + "Delegation", + PARSED_TX_STAKING_INCOMING.create_staker_or_update_staker.delegation, + }); +UX_OPTIONAL_STEP_NOCB( + ux_staking_incoming_flow_update_staker_reactivate_all_stake_step, + paging, + ux_transaction_staking_incoming_has_update_staker_reactivate_all_stake_entry(), + { + "Reactivate all Stake", + PARSED_TX_STAKING_INCOMING.create_staker_or_update_staker + .update_staker_reactivate_all_stake, + }); + +UX_FLOW(ux_transaction_staking_incoming_flow, + &ux_transaction_generic_flow_transaction_type_step, + &ux_transaction_generic_flow_amount_step, // optional, not shown for signaling tx + &ux_staking_incoming_flow_set_active_stake_or_retire_stake_amount_step, // optional, for amount in signaling data + &ux_staking_incoming_flow_staker_address_step, // optional + &ux_staking_incoming_flow_create_staker_or_update_staker_delegation_step, // optional + &ux_staking_incoming_flow_update_staker_reactivate_all_stake_step, // optional + &ux_transaction_generic_flow_fee_step, // optional + &ux_transaction_generic_flow_network_step, + &ux_transaction_generic_flow_approve_step, + &ux_transaction_generic_flow_reject_step +); + +////////////////////////////////////////////////////////////////////// + +// Message signing UI steps and flow + +UX_STEP_NOCB( + ux_message_flow_intro_step, + pnn, + { + &C_icon_certificate, + "Sign", + "message", + }); +UX_STEP_NOCB_INIT( + ux_message_flow_message_length_step, + paging, + { + // note: not %lu (for unsigned long int) because int is already 32bit on ledgers (see "Memory Alignment" in + // Ledger docu), additionally Ledger's own implementation of sprintf does not support %lu (see os_printf.c) + snprintf(ctx.req.msg.confirm.printedMessage, sizeof(ctx.req.msg.confirm.printedMessage), "%u", + ctx.req.msg.messageLength); + }, + { + "Message Length", + ctx.req.msg.confirm.printedMessage, + }); +UX_STEP_NOCB_INIT( + ux_message_flow_message_step, + paging, + ux_message_signing_prepare_printed_message(), + { + ctx.req.msg.confirm.printedMessageLabel, + ctx.req.msg.confirm.printedMessage, + }); +UX_OPTIONAL_STEP_CB( + ux_message_flow_display_ascii_step, + pbb, + ctx.req.msg.isPrintableAscii && ctx.req.msg.confirm.displayType != MESSAGE_DISPLAY_TYPE_ASCII, + ui_message_signing(MESSAGE_DISPLAY_TYPE_ASCII, true), + { + &C_icon_certificate, + "Display", + "as Text", + }); +UX_OPTIONAL_STEP_CB( + ux_message_flow_display_hex_step, + pbb, + ctx.req.msg.messageLength <= MAX_PRINTABLE_MESSAGE_LENGTH + && ctx.req.msg.confirm.displayType != MESSAGE_DISPLAY_TYPE_HEX, + ui_message_signing(MESSAGE_DISPLAY_TYPE_HEX, true), + { + &C_icon_certificate, + "Display", + "as Hex", + }); +UX_OPTIONAL_STEP_CB( + ux_message_flow_display_hash_step, + pbb, + ctx.req.msg.confirm.displayType != MESSAGE_DISPLAY_TYPE_HASH, + ui_message_signing(MESSAGE_DISPLAY_TYPE_HASH, true), + { + &C_icon_certificate, + "Display", + "as Hash", + }); +UX_STEP_CB( + ux_message_flow_approve_step, + pbb, + { + on_message_approved(); + ui_menu_main(); + }, + { + &C_icon_validate_14, + "Sign", + "message", + }); +UX_STEP_CB( + ux_message_flow_reject_step, + pb, + { + on_rejected(); + ui_menu_main(); + }, + { + &C_icon_crossmark, + "Reject", + }); + +UX_FLOW(ux_message_flow, + &ux_message_flow_intro_step, + &ux_message_flow_message_length_step, + &ux_message_flow_message_step, + &ux_message_flow_display_ascii_step, + &ux_message_flow_display_hex_step, + &ux_message_flow_display_hash_step, + &ux_message_flow_approve_step, + &ux_message_flow_reject_step +); + +////////////////////////////////////////////////////////////////////// + +void ui_menu_main() { + // reserve a display stack slot if none yet. + // The stack is for stacking UIs like the app UI, lock screen, screen saver, battery level warning, etc. + if(G_ux.stack_count == 0) { + ux_stack_push(); + } + ux_flow_init(0, ux_idle_flow, NULL); +} + +void ui_public_key() { + ux_flow_init(0, ux_public_key_flow, NULL); +} + +void ui_transaction_signing() { + // The complete title will be "Confirm " + switch (PARSED_TX.transaction_label_type) { + case TRANSACTION_LABEL_TYPE_REGULAR_TRANSACTION: + COPY_FIXED_SIZE(PARSED_TX.transaction_label, "Transaction"); + break; + case TRANSACTION_LABEL_TYPE_CASHLINK: + COPY_FIXED_SIZE(PARSED_TX.transaction_label, "Cashlink"); + break; + case TRANSACTION_LABEL_TYPE_VESTING_CREATION: + COPY_FIXED_SIZE(PARSED_TX.transaction_label, "Vesting"); + break; + case TRANSACTION_LABEL_TYPE_HTLC_CREATION: + COPY_FIXED_SIZE(PARSED_TX.transaction_label, "HTLC / Swap"); + break; + case TRANSACTION_LABEL_TYPE_STAKING_CREATE_STAKER: + COPY_FIXED_SIZE(PARSED_TX.transaction_label, "Create Staker"); + break; + case TRANSACTION_LABEL_TYPE_STAKING_ADD_STAKE: + COPY_FIXED_SIZE(PARSED_TX.transaction_label, "Add Stake"); + break; + case TRANSACTION_LABEL_TYPE_STAKING_UPDATE_STAKER: + COPY_FIXED_SIZE(PARSED_TX.transaction_label, "Update Staker"); + break; + case TRANSACTION_LABEL_TYPE_STAKING_SET_ACTIVE_STAKE: + COPY_FIXED_SIZE(PARSED_TX.transaction_label, "Set Active Stake"); + break; + case TRANSACTION_LABEL_TYPE_STAKING_RETIRE_STAKE: + COPY_FIXED_SIZE(PARSED_TX.transaction_label, "Retire Stake"); + break; + case TRANSACTION_LABEL_TYPE_STAKING_REMOVE_STAKE: + COPY_FIXED_SIZE(PARSED_TX.transaction_label, "Unstake"); + break; + default: + // This should not happen, as the transaction parser should have set a valid transaction label type. + LEDGER_ASSERT( + false, + "Invalid transaction label type" + ); + } + + const ux_flow_step_t* const * transaction_flow; + switch (PARSED_TX.transaction_type) { + case TRANSACTION_TYPE_NORMAL: + case TRANSACTION_TYPE_STAKING_OUTGOING: + transaction_flow = ux_transaction_normal_or_staking_outgoing_flow; + break; + case TRANSACTION_TYPE_VESTING_CREATION: + transaction_flow = ux_transaction_vesting_creation_flow; + break; + case TRANSACTION_TYPE_HTLC_CREATION: + transaction_flow = ux_transaction_htlc_creation_flow; + break; + case TRANSACTION_TYPE_STAKING_INCOMING: + transaction_flow = ux_transaction_staking_incoming_flow; + break; + default: + // This should not happen, as the transaction parser should have set a valid transaction type. + LEDGER_ASSERT( + false, + "Invalid transaction type" + ); + } + + ux_flow_init(0, transaction_flow, NULL); +} + +void ui_message_signing(message_display_type_t messageDisplayType, bool startAtMessageDisplay) { + ctx.req.msg.confirm.displayType = messageDisplayType; + ux_flow_init(0, ux_message_flow, startAtMessageDisplay ? &ux_message_flow_message_step : NULL); +} + +// resolve io_seproxyhal_display as io_seproxyhal_display_default +void io_seproxyhal_display(const bagl_element_t *element) { + io_seproxyhal_display_default((bagl_element_t *)element); +} + +#endif // HAVE_BAGL diff --git a/src/nimiq_ux_bagl_macros.h b/src/nimiq_ux_bagl_macros.h new file mode 100644 index 0000000..cf47dfe --- /dev/null +++ b/src/nimiq_ux_bagl_macros.h @@ -0,0 +1,87 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifndef _NIMIQ_UX_BAGL_MACROS_H_ +#define _NIMIQ_UX_BAGL_MACROS_H_ + +#ifdef HAVE_BAGL + +#include "ux.h" + +/** + * Similar to UX_STEP_NOCB defined in ux_flow_engine.h but with a special init method that displays this step only if + * the given condition is fulfilled and skips it otherwise by automatically going to the next or previous step. + */ +// Code inspired by UX_STEP_NOCB_INIT +#define UX_OPTIONAL_STEP_NOCB(stepname, layoutkind, display_condition, ...) \ + void stepname ##_init (unsigned int stack_slot) { \ + if (display_condition) { \ + ux_layout_ ## layoutkind ## _init(stack_slot); \ + } else { \ + if ( \ + /* going forward and we're not the last step */ \ + (ux_flow_direction() != FLOW_DIRECTION_BACKWARD && !ux_flow_is_last()) \ + /* we're the first step, thus go to the next */ \ + || ux_flow_is_first() \ + ) { \ + ux_flow_next(); \ + } else { \ + ux_flow_prev(); \ + } \ + } \ + } \ + const ux_layout_ ## layoutkind ## _params_t stepname ##_val = __VA_ARGS__; \ + const ux_flow_step_t stepname = { \ + stepname ## _init, \ + & stepname ## _val, \ + NULL, \ + NULL, \ + } + +/** + * Similar to UX_STEP_CB defined in ux_flow_engine.h but with a special init method that displays this step only if + * the given condition is fulfilled and skips it otherwise by automatically going to the next or previous step. + */ +#define UX_OPTIONAL_STEP_CB(stepname, layoutkind, display_condition, validate_cb, ...) \ + UX_FLOW_CALL(stepname ## _validate, { validate_cb; }) \ + void stepname ##_init (unsigned int stack_slot) { \ + if (display_condition) { \ + ux_layout_ ## layoutkind ## _init(stack_slot); \ + } else { \ + if ( \ + /* going forward and we're not the last step */ \ + (ux_flow_direction() != FLOW_DIRECTION_BACKWARD && !ux_flow_is_last()) \ + /* we're the first step, thus go to the next */ \ + || ux_flow_is_first() \ + ) { \ + ux_flow_next(); \ + } else { \ + ux_flow_prev(); \ + } \ + } \ + } \ + const ux_layout_ ## layoutkind ## _params_t stepname ##_val = __VA_ARGS__; \ + const ux_flow_step_t stepname = { \ + stepname ## _init, \ + & stepname ## _val, \ + stepname ## _validate, \ + NULL, \ + } + +#endif // HAVE_BAGL + +#endif // _NIMIQ_UX_BAGL_MACROS_H_ diff --git a/src/nimiq_ux_nbgl.c b/src/nimiq_ux_nbgl.c new file mode 100644 index 0000000..2ea4ecf --- /dev/null +++ b/src/nimiq_ux_nbgl.c @@ -0,0 +1,410 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifdef HAVE_NBGL + +// From Ledger SDK +#include "ux.h" +#include "nbgl_use_case.h" + +#include "nimiq_ux.h" +#include "globals.h" +#include "nimiq_ux_utils_transaction_signing.h" +#include "nimiq_ux_utils_message_signing.h" + +// These are declared in main.c +void on_rejected(); +void on_address_approved(); +void on_transaction_approved(); +void on_message_approved(); +void app_exit(); + +// Main menu and about menu + +#define ABOUT_MENU_INFO_COUNT 2 +// These const globals are stored in the read-only data segment of the app, i.e. in the flash memory and not in RAM, +// therefore we don't need to do any memory optimizations here (e.g. storing it in a struct union with other data). Also +// note that we don't need to mind the usage of const pointers ABOUT_MENU_INFO_TYPES and ABOUT_MENU_INFO_CONTENTS in +// const ABOUT_MENU_INFO_LIST with regards to the link address of the pointers being different from the runtime address +// (see documentation) as ngbl_use_case.h takes care of this by using the PIC macro. +static const char* const ABOUT_MENU_INFO_TYPES[ABOUT_MENU_INFO_COUNT] = {"Version", "Developer"}; +static const char* const ABOUT_MENU_INFO_CONTENTS[ABOUT_MENU_INFO_COUNT] = {APPVERSION, /* "Nimiq" */ APPNAME}; +static const nbgl_contentInfoList_t ABOUT_MENU_INFO_LIST = { + .nbInfos = ABOUT_MENU_INFO_COUNT, + .infoTypes = ABOUT_MENU_INFO_TYPES, + .infoContents = ABOUT_MENU_INFO_CONTENTS, +}; +#undef ABOUT_MENU_INFO_COUNT + +void ui_menu_main() { + nbgl_useCaseHomeAndSettings( + /* appName */ APPNAME, + /* appIcon */ &C_app_nimiq_64px, + /* tagline */ NULL, // use default description + /* initSettingPage */ INIT_HOME_PAGE, // start at the first page + /* settingContents */ NULL, // no settings + /* infosList */ &ABOUT_MENU_INFO_LIST, + /* action */ NULL, // no additional action button + /* quitCallback */ app_exit + ); +} + +////////////////////////////////////////////////////////////////////// + +// Address confirmation UI + +static void on_address_reviewed(bool approved) { + if (approved) { + on_address_approved(), + nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_VERIFIED, ui_menu_main); + } else { + on_rejected(), + nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_REJECTED, ui_menu_main); + } +} + +void ui_public_key() { + // Format address into three blocks per line by manually replacing the appropriate spaces with newlines. Note that + // this also affects the rendered QR code, which is fine though as the Nimiq address format is agnostic to the type + // of whitespace used. + ctx.req.pk.address[14] = '\n'; + ctx.req.pk.address[29] = '\n'; + nbgl_useCaseAddressReview( + /* address */ ctx.req.pk.address, + /* additionalTagValueList */ NULL, // no additional infos + /* icon */ &C_app_nimiq_64px, + /* reviewTitle */ "Verify NIM address", + /* reviewSubtitle */ "After copying and pasting, or scanning the address, make sure that it matches what's " + "shown on your Ledger.", + /* choiceCallback */ on_address_reviewed + ); +} + +////////////////////////////////////////////////////////////////////// + +// NBGL review utils + +// The review with the most potential entries is the vesting creation flow. The maximum entries it can display are: +// amount, owner address, start, period, step count, step duration, first step duration, step amount, first step amount, +// last step amount, fee, network. Note that the single-block block entry and the multi-block vesting entries can not +// appear at the same time, same for the entry for the pre-vested amount and first step amount. +#define REVIEW_ENTRIES_MAX_COUNT 12 +static struct { + nbgl_contentTagValue_t entries[REVIEW_ENTRIES_MAX_COUNT]; + uint8_t count; +} review_entries; + +static void review_entries_initialize() { + // Initialize the structure with zeroes, including setting the count to 0, and the entries' .forcePageStart (don't + // enforce a new page by default), .centeredInfo (don't center entry vertically) and .aliasValue (display full + // values and no alias) to 0 / false. + memset(&review_entries, 0, sizeof(review_entries)); +} + +static void review_entries_add(const char *item, const char *value) { + LEDGER_ASSERT( + review_entries.count < REVIEW_ENTRIES_MAX_COUNT, + "Too many nbgl ui entries" + ); + review_entries.entries[review_entries.count].item = item; + review_entries.entries[review_entries.count].value = value; + review_entries.count++; +} +#undef REVIEW_ENTRIES_MAX_COUNT + +static void review_entries_add_optional(const char *item, const char *value, bool condition) { + if (!condition) return; + review_entries_add(item, value); +} + +static void review_entries_launch_use_case_review( + nbgl_operationType_t operation_type, + const nbgl_icon_details_t *icon, + const char *review_title, + const char *review_subtitle, + const char *finish_title, + nbgl_choiceCallback_t choice_callback, + bool use_small_font +) { + // The tagValueList gets copied in nbgl_useCaseReview, therefore it's sufficient to initialize it only in temporary + // memory. We use G_io_apdu buffer as temporary buffer, to save some stack space, and check that it can fit the data + // with a compile time assertion. Note that the entries are not copied, and we therefore have them in global memory. + _Static_assert( + sizeof(nbgl_contentTagValueList_t) <= sizeof(G_io_apdu_buffer), + "G_io_apdu_buffer can't fit review list\n" + ); + nbgl_contentTagValueList_t *temporary_tag_value_list_pointer = (nbgl_contentTagValueList_t*) G_io_apdu_buffer; + // Initialize list with zeroes, including .nbMaxLinesForValue (no limit of lines to display). + memset(temporary_tag_value_list_pointer, 0, sizeof(nbgl_contentTagValueList_t)); + temporary_tag_value_list_pointer->wrapping = true; // Prefer wrapping on spaces, e.g. for nicer address formatting. + temporary_tag_value_list_pointer->smallCaseForValue = use_small_font; + temporary_tag_value_list_pointer->pairs = review_entries.entries; + temporary_tag_value_list_pointer->nbPairs = review_entries.count; + + nbgl_useCaseReview( + operation_type, + temporary_tag_value_list_pointer, + icon, + review_title, + review_subtitle, + finish_title, + choice_callback + ); + + // Make sure that the list was in fact copied, to detect if that ever changes internally in nbgl_useCaseReview. + // Unfortunately, we can't easily memcmp via a pointer to where the data is copied to, as the data is copied to a + // static variable in nbgl_use_case.c and there's also no non-static method that exposes pointers to it. Instead, we + // reset the temporary buffer, to make it immediately noticeable via Ragger end-to-end tests if the data was not + // copied. Use explicit_bzero instead of memset to avoid this being optimized away. + explicit_bzero(temporary_tag_value_list_pointer, sizeof(nbgl_contentTagValueList_t)); +} + +////////////////////////////////////////////////////////////////////// + +// Transaction signing UI + +static void ui_transaction_prepare_review_entries_normal_or_staking_outgoing() { + review_entries_initialize(); + review_entries_add_optional( + "Amount", + PARSED_TX.value, + ux_transaction_generic_has_amount_entry() + ); + review_entries_add( + "Recipient", + PARSED_TX_NORMAL_OR_STAKING_OUTGOING.recipient + ); + review_entries_add_optional( + PARSED_TX_NORMAL_OR_STAKING_OUTGOING.extra_data_label, + PARSED_TX_NORMAL_OR_STAKING_OUTGOING.extra_data, + ux_transaction_normal_or_staking_outgoing_has_data_entry() + ); + review_entries_add_optional( + "Fee", + PARSED_TX.fee, + ux_transaction_generic_has_fee_entry() + ); + review_entries_add( + "Network", + PARSED_TX.network + ); +} + +static void ui_transaction_prepare_review_entries_staking_incoming() { + review_entries_initialize(); + // Amount for non-signaling transactions + review_entries_add_optional( + "Amount", + PARSED_TX.value, + ux_transaction_generic_has_amount_entry() + ); + // Amount in incoming staking data for signaling transactions + review_entries_add_optional( + "Amount", + PARSED_TX_STAKING_INCOMING.set_active_stake_or_retire_stake.amount, + ux_transaction_staking_incoming_has_set_active_stake_or_retire_stake_amount_entry() + ); + review_entries_add_optional( + "Staker", + PARSED_TX_STAKING_INCOMING.validator_or_staker_address, + ux_transaction_staking_incoming_has_staker_address_entry() + ); + review_entries_add_optional( + "Delegation", + PARSED_TX_STAKING_INCOMING.create_staker_or_update_staker.delegation, + ux_transaction_staking_incoming_has_create_staker_or_update_staker_delegation_entry() + ); + review_entries_add_optional( + "Reactivate all Stake", + PARSED_TX_STAKING_INCOMING.create_staker_or_update_staker + .update_staker_reactivate_all_stake, + ux_transaction_staking_incoming_has_update_staker_reactivate_all_stake_entry() + ); + review_entries_add_optional( + "Fee", + PARSED_TX.fee, + ux_transaction_generic_has_fee_entry() + ); + review_entries_add( + "Network", + PARSED_TX.network + ); +} + +static void on_transaction_reviewed(bool approved) { + if (approved) { + on_transaction_approved(), + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_SIGNED, ui_menu_main); + } else { + on_rejected(), + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_REJECTED, ui_menu_main); + } +} + +void ui_transaction_signing() { + // Pointers to pre-existing const strings in read-only data segment / flash memory. Not meant to be written to. + // While the strings are a bit repetitive, and thus somewhat wasteful in flash memory, we still prefer this approach + // over assembling the string in RAM memory, because that is the much more limited resource. Manual newlines are set + // for a nicer layout of the texts. + const char *review_title; + const char *finish_title; + const char *review_subtitle = NULL; // none by default + switch (PARSED_TX.transaction_label_type) { + case TRANSACTION_LABEL_TYPE_REGULAR_TRANSACTION: + review_title = "Review transaction\nto send NIM"; + finish_title = "Sign transaction\nto send NIM"; + break; + case TRANSACTION_LABEL_TYPE_CASHLINK: + review_title = "Review\nCashlink creation"; + finish_title = "Sign\nCashlink creation"; + break; + case TRANSACTION_LABEL_TYPE_VESTING_CREATION: + review_title = "Review\nVesting Contract creation"; + finish_title = "Sign\nVesting Contract creation"; + break; + case TRANSACTION_LABEL_TYPE_HTLC_CREATION: + review_title = "Review\nHTLC creation / Swap"; + finish_title = "Sign\nHTLC creation / Swap"; + break; + case TRANSACTION_LABEL_TYPE_STAKING_CREATE_STAKER: + review_title = "Review staking setup\nto start staking"; + finish_title = "Sign staking setup\nto start staking"; + break; + case TRANSACTION_LABEL_TYPE_STAKING_ADD_STAKE: + review_title = "Review\naddition of stake"; + finish_title = "Sign\naddition of stake"; + review_subtitle = "This transaction increases your amount of NIM assigned for staking."; + break; + case TRANSACTION_LABEL_TYPE_STAKING_UPDATE_STAKER: + review_title = "Review update\nof staking setup"; + finish_title = "Sign update\nof staking setup"; + review_subtitle = "This transaction updates your current staking setup with the following changes."; + break; + case TRANSACTION_LABEL_TYPE_STAKING_SET_ACTIVE_STAKE: + review_title = "Review setting\nactive stake amount"; + finish_title = "Sign setting\nactive stake amount"; + review_subtitle = "This transaction sets the NIM amount that will be actively used for staking. Set it to " + "0, if you want to change the validator you're delegating to."; + break; + case TRANSACTION_LABEL_TYPE_STAKING_RETIRE_STAKE: + review_title = "Review retiring\nstaked NIM"; + finish_title = "Sign retiring\nstaked NIM"; + review_subtitle = "This transaction will disable the given amount of NIM from staking. This needs to be " + "done first, if you want to later withdraw them."; + break; + case TRANSACTION_LABEL_TYPE_STAKING_REMOVE_STAKE: + review_title = "Review withrawal\nof previously staked NIM"; + finish_title = "Sign withdrawal\nof previously staked NIM"; + break; + default: + // This should not happen, as the transaction parser should have set a valid transaction label type. + LEDGER_ASSERT( + false, + "Invalid transaction label type" + ); + } + + switch (PARSED_TX.transaction_type) { + case TRANSACTION_TYPE_NORMAL: + case TRANSACTION_TYPE_STAKING_OUTGOING: + ui_transaction_prepare_review_entries_normal_or_staking_outgoing(); + break; + case TRANSACTION_TYPE_STAKING_INCOMING: + ui_transaction_prepare_review_entries_staking_incoming(); + break; + case TRANSACTION_TYPE_VESTING_CREATION: + case TRANSACTION_TYPE_HTLC_CREATION: + default: + // This should not happen, as the transaction parser should have set a valid transaction type. + LEDGER_ASSERT( + false, + "Invalid transaction type" + ); + } + + review_entries_launch_use_case_review( + /* operation_type */ TYPE_TRANSACTION, + /* icon */ &C_app_nimiq_64px, + /* review_title */ review_title, + /* review_subtitle */ review_subtitle, + /* finish_title */ finish_title, + /* choice_callback */ on_transaction_reviewed, + /* use_small_font */ false + ); +} + +////////////////////////////////////////////////////////////////////// + +// Message signing UI + +static void ui_message_prepare_review_entries(message_display_type_t messageDisplayType) { + review_entries_initialize(); + ctx.req.msg.confirm.displayType = messageDisplayType; // used in ux_message_signing_prepare_printed_message + ux_message_signing_prepare_printed_message(); + review_entries_add( + ctx.req.msg.confirm.printedMessageLabel, + ctx.req.msg.confirm.printedMessage + ); +} + +// Called when long press button on 3rd page is long-touched or when reject footer is touched. +static void on_message_reviewed(bool approved) { + if (approved) { + on_message_approved(), + nbgl_useCaseReviewStatus(STATUS_TYPE_MESSAGE_SIGNED, ui_menu_main); + } else { + on_rejected(), + nbgl_useCaseReviewStatus(STATUS_TYPE_MESSAGE_REJECTED, ui_menu_main); + } +} + +void ui_message_signing(message_display_type_t messageDisplayType, bool startAtMessageDisplay) { + UNUSED(startAtMessageDisplay); + + // Pointer to pre-existing const strings in read-only data segment / flash memory. Not meant to be written to. + const char *review_subtitle; + switch (messageDisplayType) { + case MESSAGE_DISPLAY_TYPE_ASCII: + review_subtitle = NULL; // none + break; + case MESSAGE_DISPLAY_TYPE_HEX: + review_subtitle = "The message is displayed in HEX format."; + break; + case MESSAGE_DISPLAY_TYPE_HASH: + review_subtitle = "The message is displayed as SHA-256 hash."; + break; + default: + // This should not happen, as the request parser should have set a valid message display type. + LEDGER_ASSERT( + false, + "Invalid message display type" + ); + } + ui_message_prepare_review_entries(messageDisplayType); + + review_entries_launch_use_case_review( + /* operation_type */ TYPE_MESSAGE, + /* icon */ &C_Review_64px, // provided by ledger-secure-sdk + /* review_title */ "Review message", + /* review_subtitle */ review_subtitle, + /* finish_title */ "Sign message", + /* choice_callback */ on_message_reviewed, + /* use_small_font */ true + ); +} + +#endif // HAVE_NBGL diff --git a/src/nimiq_ux_utils_message_signing.c b/src/nimiq_ux_utils_message_signing.c new file mode 100644 index 0000000..def9b09 --- /dev/null +++ b/src/nimiq_ux_utils_message_signing.c @@ -0,0 +1,46 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#include "nimiq_ux_utils_message_signing.h" +#include "globals.h" + +void ux_message_signing_prepare_printed_message() { + // No errors are expected here, as all data has already been verified in handleSignMessage + switch (ctx.req.msg.confirm.displayType) { + case MESSAGE_DISPLAY_TYPE_ASCII: + COPY_FIXED_SIZE(ctx.req.msg.confirm.printedMessageLabel, "Message"); + memmove(ctx.req.msg.confirm.printedMessage, ctx.req.msg.printableMessage, ctx.req.msg.messageLength); + ctx.req.msg.confirm.printedMessage[ctx.req.msg.messageLength] = '\0'; // string terminator + break; + case MESSAGE_DISPLAY_TYPE_HEX: + COPY_FIXED_SIZE(ctx.req.msg.confirm.printedMessageLabel, "Message Hex"); + LEDGER_ASSERT( + print_hex(ctx.req.msg.printableMessage, ctx.req.msg.messageLength, ctx.req.msg.confirm.printedMessage, + sizeof(ctx.req.msg.confirm.printedMessage)) == ERROR_NONE, + "Failed to print message hex" + ); + break; + case MESSAGE_DISPLAY_TYPE_HASH: + COPY_FIXED_SIZE(ctx.req.msg.confirm.printedMessageLabel, "Message Hash"); + LEDGER_ASSERT( + print_hex(ctx.req.msg.confirm.messageHash, sizeof(ctx.req.msg.confirm.messageHash), + ctx.req.msg.confirm.printedMessage, sizeof(ctx.req.msg.confirm.printedMessage)) == ERROR_NONE, + "Failed to print message hash" + ); + break; + } +} diff --git a/src/nimiq_ux_utils_message_signing.h b/src/nimiq_ux_utils_message_signing.h new file mode 100644 index 0000000..72f4a90 --- /dev/null +++ b/src/nimiq_ux_utils_message_signing.h @@ -0,0 +1,26 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifndef _NIMIQ_UX_UTILS_MESSAGE_SIGNING_H_ +#define _NIMIQ_UX_UTILS_MESSAGE_SIGNING_H_ + +#include "constants.h" +#include "error_macros.h" + +void ux_message_signing_prepare_printed_message(); + +#endif //_NIMIQ_UX_UTILS_MESSAGE_SIGNING_H_ diff --git a/src/nimiq_ux_utils_transaction_signing.c b/src/nimiq_ux_utils_transaction_signing.c new file mode 100644 index 0000000..6fd1cfc --- /dev/null +++ b/src/nimiq_ux_utils_transaction_signing.c @@ -0,0 +1,212 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#include +#include + +#include "nimiq_ux_utils_transaction_signing.h" +#include "globals.h" + +bool ux_transaction_generic_has_amount_entry() { + // The transaction amount can be 0 for signaling transactions, in which case we want to show the amount given in the + // IncomingStakingTransactionData instead, if applicable. + return strcmp(PARSED_TX.value, "0 NIM") != 0; +} + +bool ux_transaction_generic_has_fee_entry() { + return strcmp(PARSED_TX.fee, "0 NIM") != 0; +} + +bool ux_transaction_normal_or_staking_outgoing_has_data_entry() { + return strlen(PARSED_TX_NORMAL_OR_STAKING_OUTGOING.extra_data); +} + +// HTLC creation specific UI steps and flow +// As HTLCs are quite technical, we try to not display the less relevant information to the user. +// Considerations for which data can be safely skipped under which circumstances: +// - transaction recipient address (not to be confused with the htlc recipient address, also called redeem address): +// The recipient address for the contract creation must be the contract address which is deterministically calculated +// from the other transaction parameters. Any transaction with a different recipient address than the expected address +// is rejected by the Nimiq network which does therefore not need to be displayed or checked. +// - htlc refund address (also called htlc sender; not to be confused with the transaction sender): +// If the refund address equals the transaction sender, we omit display because then the funds can be refunded to +// where they came from, which is an address of a BasicAccount or MultiSig under (partial) control of this Ledger. +// Note that any other address under the control of this Ledger could also be whitelisted, but currently the refund +// address being equal to the transaction sender is the normal case in our current use cases and whitelisting other +// Ledger addresses would require transmitting the refund address key path with the request, such that we can verify +// that the address is one under control of this Ledger. +// - hash algorithm: +// As the user confirms the hash root, an attacker trying to let the user create a htlc with the wrong hash algorithm +// would need to know the pre-image for the hash root for the specified algorithm to be able to gain access to the +// funds which is close to impossible unless he's the legitimate recipient anyways who specified the confirmed hash +// root. The worst that can happen, is that the funds are locked until the timeout at which point they can be redeemed +// by the refund address owner. To avoid that the refund address owner as attacker could take advantage of making it +// impossible for the redeem address owner to redeem the funds, we skip the hash algorithm display only if the refund +// address is our address (see above). As an additional restriction, we also skip the display of the hash algorithm +// only if it's sha256 which is the commonly used hash algorithm and if the funds are not locked for a long time. +// - hash count (here called hash steps): +// Specifying a lower hash count than the actual intended hash count allows the htlc redeem address owner as attacker +// to redeem more funds per pre-image step than intended. However, if the user's machine creating the manipulated +// transaction to be signed on the Ledger is compromised, also usually the htlc secret (unhashed pre-image) which is +// usually on the same machine is compromised such that the hash count doesn't matter anymore. I.e. if the htlc secret +// is compromised, the hash count yields no protection of the funds anymore. +// Specifying a higher hash count than the intended one effectively locks part of the funds for the redeem address +// owner until they become available to the refund address owner after the timeout. To avoid that the refund address +// owner as attacker could take advantage of blocking funds to the redeem address owner, we skip the display of the +// hash count only if the refund address is our address (see above). Additionally, to avoid that funds are potentially +// locked via a higher hash count for a long time, we only skip the display for short timeouts. However, as blocking +// the funds requires a higher hash count than the actual one, for 1, the lowest possible hash count, we never have to +// display it. +// - timeout (here called htlc expiry block): +// The timeout specifies for how long funds will be locked if not redeemed until they are refundable. As short +// timeouts (which should be the case for most practically used htlcs) are favorable for the user if the refund +// address is his, we don't display short timeouts. To avoid that another refund address owner as attacker could take +// advantage of a short or already passed timeout, we skip the timeout display only if the refund address is our +// address (see above). + +bool ux_transaction_htlc_creation_has_refund_address_entry() { + return !PARSED_TX_HTLC_CREATION.is_refund_address_sender_address; +} + +bool ux_transaction_htlc_creation_has_hash_algorithm_entry() { + return !PARSED_TX_HTLC_CREATION.is_refund_address_sender_address + || !PARSED_TX_HTLC_CREATION.is_timing_out_soon + || !PARSED_TX_HTLC_CREATION.is_using_sha256; +} + +bool ux_transaction_htlc_creation_has_hash_count_entry() { + return strcmp(PARSED_TX_HTLC_CREATION.hash_count, "1") != 0 + && (!PARSED_TX_HTLC_CREATION.is_refund_address_sender_address || !PARSED_TX_HTLC_CREATION.is_timing_out_soon); +} + +bool ux_transaction_htlc_creation_has_timeout_entry() { + return !PARSED_TX_HTLC_CREATION.is_refund_address_sender_address + || !PARSED_TX_HTLC_CREATION.is_timing_out_soon; +} + +// Vesting Contract Creation specific UI steps and flow +// Other than for HTLCs we generally do not try to skip less relevant data as vesting contracts are only rarely created +// and all parameters are similarly important. However, depending on the specific vesting contract parameters, some data +// is redundant. Specifically, we have the following optimizations: +// - vesting owner: +// Display of the vesting owner address is skipped if it equals the transaction sender address. +// - for 0 steps (all funds are pre-vested): +// We only show the info about the pre-vested amount and skip all other data. +// - for 1 step (all funds unlock at a specific block): +// We show a special entry with the vesting block. Additionally, the step for a pre-vested amount might be shown. All +// other info is redundant and skipped. +// - for 2 steps: +// If first step amount and last step amount differ from regular step amount, do not display what would be the regular +// step amount as all steps differ from that. + +bool ux_transaction_vesting_creation_has_owner_address_entry() { + return !PARSED_TX_VESTING_CREATION.is_owner_address_sender_address; +} + +bool ux_transaction_vesting_creation_has_single_vesting_block_entry() { + // simplified ui for step_count == 1 case + return !PARSED_TX_VESTING_CREATION.is_multi_step; +} + +bool ux_transaction_vesting_creation_has_start_and_period_and_step_count_and_step_duration_entries() { + return PARSED_TX_VESTING_CREATION.is_multi_step; +} + +bool ux_transaction_vesting_creation_has_first_step_duration_entry() { + return PARSED_TX_VESTING_CREATION.is_multi_step + // The first step duration is different from the regular step duration. + && strcmp(PARSED_TX_VESTING_CREATION.first_step_block_count, PARSED_TX_VESTING_CREATION.step_block_count) != 0; +} + +bool ux_transaction_vesting_creation_has_step_amount_entry() { + return PARSED_TX_VESTING_CREATION.is_multi_step + // Skip if step_count == 2 and both steps differ from what would be the regular step amount. + && !( + strcmp(PARSED_TX_VESTING_CREATION.step_count, "2") == 0 + && strcmp(PARSED_TX_VESTING_CREATION.first_step_amount, PARSED_TX_VESTING_CREATION.step_amount) != 0 + && strcmp(PARSED_TX_VESTING_CREATION.last_step_amount, PARSED_TX_VESTING_CREATION.step_amount) != 0 + ); +} + +bool ux_transaction_vesting_creation_has_first_step_amount_entry() { + return PARSED_TX_VESTING_CREATION.is_multi_step + // The first step amount is different from the regular step amount. + && strcmp(PARSED_TX_VESTING_CREATION.first_step_amount, PARSED_TX_VESTING_CREATION.step_amount) != 0; +} + +bool ux_transaction_vesting_creation_has_last_step_amount_entry() { + return PARSED_TX_VESTING_CREATION.is_multi_step + // The last step amount is different from the regular step amount. + && strcmp(PARSED_TX_VESTING_CREATION.last_step_amount, PARSED_TX_VESTING_CREATION.step_amount) != 0; +} + +bool ux_transaction_vesting_creation_has_pre_vested_amount_entry() { + return strcmp(PARSED_TX_VESTING_CREATION.pre_vested_amount, "0 NIM") != 0; +} + +// Incoming staking transaction (transactions to the staking contract) specific UI steps and flow +// Considerations for which data can be safely skipped under which circumstances: +// - 0 NIM transaction amount for signaling transactions. If the incoming staking data includes an amount, that is shown +// instead. +// - transaction recipient address as this must be the staking contract. +// - validator address (from validator signature proof): is always displayed as validator management is considered an +// advanced feature, where the user probably wants to have a complete overview of the transaction data. It's also not +// a very regularly occurring / common transaction. +// - staker address (from staker signature proof): If the staker address equals the transaction sender, we omit display +// because then the staker is under control of the user / this Ledger (in case of multi-sig, partial control). This is +// also the most common case. We don't need to check that the sender address actually belongs to the Ledger account as +// a signature for a wrong sender address will be rejected by the network. This also covers contracts as sender as the +// staker address can't really be the contract address because for the deterministic contract address, no signing key +// is known which could create the valid staker signature proof. Also, multi-sig sender addresses are theoretically +// covered, i.e. the mutli-sig signed transactions also assume the multi-sig as staker owner by default, however multi +// sig staker signature proofs are not currently supported as they'd require a non-empty merkle path. Note that any +// other address under the control of this Ledger could also be whitelisted, but currently the staker address being +// equal to the transaction sender is the normal case in our current use cases and whitelisting other Ledger addresses +// would require transmitting the staker address key path with the request, such that we can verify that the address +// is one under control of this Ledger. +// - no other parts of the signature proof need to be displayed to the user as invalid signature proofs will be rejected +// by the network nodes. +// - delegation addresses: are always displayed as the assumption is that common, non-advanced users would not delegate +// to themselves, and advanced users would like to see this information, even if delegating to themselves. + +bool ux_transaction_staking_incoming_has_set_active_stake_or_retire_stake_amount_entry() { + // Show if it's a data type that specifies an amount in the data. Note that these are signaling transactions. I.e. + // the regular transaction amount is 0, and not displayed. + return PARSED_TX_STAKING_INCOMING.type == SET_ACTIVE_STAKE + || PARSED_TX_STAKING_INCOMING.type == RETIRE_STAKE; +} + +bool ux_transaction_staking_incoming_has_staker_address_entry() { + // Show if it's a staker tx, as opposed to a validator tx, and staker address is set (i.e. it's different to sender) + return (PARSED_TX_STAKING_INCOMING.type == CREATE_STAKER + || PARSED_TX_STAKING_INCOMING.type == ADD_STAKE + || PARSED_TX_STAKING_INCOMING.type == UPDATE_STAKER + || PARSED_TX_STAKING_INCOMING.type == SET_ACTIVE_STAKE + || PARSED_TX_STAKING_INCOMING.type == RETIRE_STAKE + ) && strlen(PARSED_TX_STAKING_INCOMING.validator_or_staker_address); +} + +bool ux_transaction_staking_incoming_has_create_staker_or_update_staker_delegation_entry() { + // Show if it's a data type that potentially specifies a delegation address, and it is set. + return (PARSED_TX_STAKING_INCOMING.type == CREATE_STAKER || PARSED_TX_STAKING_INCOMING.type == UPDATE_STAKER) + && strlen(PARSED_TX_STAKING_INCOMING.create_staker_or_update_staker.delegation); +} + +bool ux_transaction_staking_incoming_has_update_staker_reactivate_all_stake_entry() { + // Show reactivate_all_stake for data type UPDATE_STAKER. + return PARSED_TX_STAKING_INCOMING.type == UPDATE_STAKER; +} diff --git a/src/nimiq_ux_utils_transaction_signing.h b/src/nimiq_ux_utils_transaction_signing.h new file mode 100644 index 0000000..7ef1338 --- /dev/null +++ b/src/nimiq_ux_utils_transaction_signing.h @@ -0,0 +1,47 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifndef _NIMIQ_UX_UTILS_TRANSACTION_SIGNING_H_ +#define _NIMIQ_UX_UTILS_TRANSACTION_SIGNING_H_ + +#include + +bool ux_transaction_generic_has_amount_entry(); +bool ux_transaction_generic_has_fee_entry(); + +bool ux_transaction_normal_or_staking_outgoing_has_data_entry(); + +bool ux_transaction_htlc_creation_has_refund_address_entry(); +bool ux_transaction_htlc_creation_has_hash_algorithm_entry(); +bool ux_transaction_htlc_creation_has_hash_count_entry(); +bool ux_transaction_htlc_creation_has_timeout_entry(); + +bool ux_transaction_vesting_creation_has_owner_address_entry(); +bool ux_transaction_vesting_creation_has_single_vesting_block_entry(); +bool ux_transaction_vesting_creation_has_start_and_period_and_step_count_and_step_duration_entries(); +bool ux_transaction_vesting_creation_has_first_step_duration_entry(); +bool ux_transaction_vesting_creation_has_step_amount_entry(); +bool ux_transaction_vesting_creation_has_first_step_amount_entry(); +bool ux_transaction_vesting_creation_has_last_step_amount_entry(); +bool ux_transaction_vesting_creation_has_pre_vested_amount_entry(); + +bool ux_transaction_staking_incoming_has_set_active_stake_or_retire_stake_amount_entry(); +bool ux_transaction_staking_incoming_has_staker_address_entry(); +bool ux_transaction_staking_incoming_has_create_staker_or_update_staker_delegation_entry(); +bool ux_transaction_staking_incoming_has_update_staker_reactivate_all_stake_entry(); + +#endif // _NIMIQ_UX_UTILS_TRANSACTION_SIGNING_H_ diff --git a/src/signature_proof.c b/src/signature_proof.c new file mode 100644 index 0000000..b3151f1 --- /dev/null +++ b/src/signature_proof.c @@ -0,0 +1,67 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#include "signature_proof.h" +#include "nimiq_utils.h" + +/** + * Read a signature proof from a buffer. Note that all pointers in the returned signature proof are to the original + * buffer. No copy of the data is created. + */ +WARN_UNUSED_RESULT +bool read_signature_proof(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, + signature_proof_t *out_signature_proof) { + // See Serde::Serialize for SignatureProof in primitives/transaction/src/signature_proof.rs in core-rs-albatross. + RETURN_ON_ERROR( + !read_u8(in_out_buffer, in_out_buffer_length, &out_signature_proof->type_and_flags) + || !read_sub_buffer(32, in_out_buffer, in_out_buffer_length, &out_signature_proof->public_key) + || !read_u8(in_out_buffer, in_out_buffer_length, &out_signature_proof->merkle_path_length) + || !read_sub_buffer(64, in_out_buffer, in_out_buffer_length, &out_signature_proof->signature), + false + ); + RETURN_ON_ERROR( + out_signature_proof->type_and_flags != 0, + false, + "Only ed25519 signature proofs without flags supported\n" + ); + RETURN_ON_ERROR( + out_signature_proof->merkle_path_length != 0, + false, + "Only signature proofs with empty merkle path supported\n" + ); + return true; +} + +bool is_empty_default_signature_proof(signature_proof_t signature_proof) { + // The empty default signature proof entirely consists of bytes of value 0, see Default for SignatureProof in + // primitives/transaction/src/signature_proof.rs + if ( + // type field (algorithm and flags), by default Ed25519 (PublicKey enum value 0) and no flags set + signature_proof.type_and_flags != 0 + // Empty merkle path encoding u8 length 0, see Default and Serialize for MerklePath in utils/src/merkle/mod.rs + || signature_proof.merkle_path_length != 0 + ) return false; + // Ed25519PublicKey filled with 0s, see Default and Serialize in keys/src/public_key.rs + for (uint8_t i = 0; i < 32; i++) { + if (signature_proof.public_key[i] != 0) return false; + } + // Ed25519Signature filled with 0s, see Default and Serialize in keys/src/signature.rs + for (uint8_t i = 0; i < 64; i++) { + if (signature_proof.signature[i] != 0) return false; + } + return true; +} diff --git a/src/signature_proof.h b/src/signature_proof.h new file mode 100644 index 0000000..8f6a0cc --- /dev/null +++ b/src/signature_proof.h @@ -0,0 +1,45 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifndef _NIMIQ_SIGNATURE_PROOF_H_ +#define _NIMIQ_SIGNATURE_PROOF_H_ + +#include +#include + +#include "error_macros.h" + +typedef struct { + // Currently only ed25519 without flags and only empty merkle paths are supported, therefore: + // - the public key is an ed25519 public key and the signature an ed25519 signature + // - no WebauthnExtraFields are present + // - no merkle path compressed vector or node hashes are present. + uint8_t *public_key; // pointer to a 32 byte ed25519 public key + // uint8_t *merkle_path_compressed; // NULL or a pointer to a VecU8 + // uint8_t *merkle_path_node_hashes; // NULL or a pointer to a VecU8 + uint8_t *signature; // pointer to a 64 byte ed25519 signature + uint8_t type_and_flags; + uint8_t merkle_path_length; +} signature_proof_t; + +WARN_UNUSED_RESULT +bool read_signature_proof(uint8_t **in_out_buffer, uint16_t *in_out_buffer_length, + signature_proof_t *out_signature_proof); + +bool is_empty_default_signature_proof(signature_proof_t signature_proof); + +#endif // _NIMIQ_SIGNATURE_PROOF_H_ diff --git a/src/utility_macros.h b/src/utility_macros.h new file mode 100644 index 0000000..a2b0e1e --- /dev/null +++ b/src/utility_macros.h @@ -0,0 +1,76 @@ +/******************************************************************************* + * Ledger Nimiq App + * (c) 2018 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#ifndef _NIMIQ_UTILITY_MACROS_H_ +#define _NIMIQ_UTILITY_MACROS_H_ + +#if defined(TEST) && TEST +#define PRINTF(...) printf(__VA_ARGS__) +#define LEDGER_ASSERT(test, ...) assert(test) +#define PIC(code) code +#define TARGET_NANOS 1 +#endif // TEST + +#if defined(NIMIQ_DEBUG) && NIMIQ_DEBUG +#define DEBUG_EMIT(...) __VA_ARGS__ +#else +#define DEBUG_EMIT(...) /* drop contents */ +#endif // NIMIQ_DEBUG + +#define VA_ARGS(...) __VA_ARGS__ +#define VA_ARGS_DROP(...) /* drop contents */ +#define VA_ARGS_PICK_FIRST(first, ...) first +#define VA_ARGS_OMIT_FIRST(first, ...) __VA_ARGS__ +#define VA_ARGS_IF_NOT_EMPTY_EMIT(content, ...) __VA_OPT__(content) +#define VA_ARGS_IF_EMPTY_EMIT(content, ...) VA_ARGS##__VA_OPT__(_DROP)(content) + +#ifndef MIN +/** + * Get the minimum of two values. This should only be used with constant expressions to avoid an expression with runtime + * cost or side effects running twice. + */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif // MIN + +#ifndef MAX +/** + * Get the maximum of two values. This should only be used with constant expressions to avoid an expression with runtime + * cost or side effects running twice. + */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif // MAX + +#define STRING_LENGTH_WITH_SUFFIX(length_a, suffix) (length_a - /* string terminator of string a */ 1 + sizeof(suffix)) + +#define STRUCT_MEMBER_SIZE(struct_type, member) (sizeof(((struct_type *) 0)->member)) + +/** + * Copy data of fixed size to a destination of fixed size. This macro performs a static assertion at compile time, that + * the destination fits the data. + * In order for the static assertion to work, the sizes of source and destination have to be known at compile time, e.g. + * by being a buffer of fixed size or a constant string (for which sizeof includes the string terminator). + */ +#define COPY_FIXED_SIZE(destination, source) \ + ({ \ + _Static_assert( \ + sizeof(destination) >= sizeof(source), \ + "Copy destination too short.\n" \ + ); \ + memcpy(destination, source, sizeof(source)); \ + }) + +#endif // _NIMIQ_UTILITY_MACROS_H_ diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..ac44c3f --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,17 @@ +from ragger.conftest import configuration + +########################### +### CONFIGURATION START ### +########################### + +# You can configure optional parameters by overriding the value of ragger.configuration.OPTIONAL_CONFIGURATION +# Please refer to ragger/conftest/configuration.py for their descriptions and accepted values. +# Also see https://ledgerhq.github.io/ragger/tutorial_conftest.html and +# https://github.com/LedgerHQ/ragger?tab=readme-ov-file#with-pytest + +######################### +### CONFIGURATION END ### +######################### + +# Pull all features from the base ragger conftest using the overridden configuration +pytest_plugins = ("ragger.conftest.base_conftest", ) diff --git a/tests/errors.py b/tests/errors.py new file mode 100644 index 0000000..8cab750 --- /dev/null +++ b/tests/errors.py @@ -0,0 +1,17 @@ +from enum import IntEnum + +class Errors(IntEnum): + SW_DENY = 0x6985 + SW_WRONG_P1P2 = 0x6A86 + SW_WRONG_DATA_LENGTH = 0x6A87 + SW_INS_NOT_SUPPORTED = 0x6D00 + SW_CLA_NOT_SUPPORTED = 0x6E00 + SW_WRONG_RESPONSE_LENGTH = 0xB000 + SW_DISPLAY_BIP32_PATH_FAIL = 0xB001 + SW_DISPLAY_ADDRESS_FAIL = 0xB002 + SW_DISPLAY_AMOUNT_FAIL = 0xB003 + SW_WRONG_TX_LENGTH = 0xB004 + SW_TX_PARSING_FAIL = 0xB005 + SW_TX_HASH_FAIL = 0xB006 + SW_BAD_STATE = 0xB007 + SW_SIGNATURE_FAIL = 0xB008 diff --git a/tests/raw_apdu_exchange.py b/tests/raw_apdu_exchange.py new file mode 100644 index 0000000..f60ecd4 --- /dev/null +++ b/tests/raw_apdu_exchange.py @@ -0,0 +1,51 @@ +from typing import Generator, Union +from contextlib import contextmanager +from ragger.backend.interface import BackendInterface + +""" +A RawApduExchange specifies the input and output APDUs (excluding the status word) of a request, which can be exchanged +with a device backend, and be checked for the expected response apdu. +We use already encoded raw APDUs in our tests, instead of serializing them from input parameters of the requests, and +parsing them to the individual components of the response, because the serializing and parsing is already implemented in +Rust (Nimiq Core-albatross), C (Nimiq Ledger app), and Typescript (Nimiq Ledger API), which makes redoing all that work +again for the Python tests rather unnecessary. Instead we use encoded raw APDUs generated via the Nimiq Ledger API. +In the future, we could consider whether we actually want to serialize and parse the APDUs in the tests, by calling into +the Rust or Typescript implementation (not the C implementation, as that's the one we're testing) from python. +""" +class RawApduExchange: + input_apdus: list[bytes] + expected_response: bytes + + # Inputs and output are hex encoded raw apdus. The output does not contain the returned status word, e.g. 0x9000, + # which is in response.status instead. + def __init__(self, input_apdus: Union[str, list[str]], expected_response: str): + self.input_apdus = list(map(bytes.fromhex, input_apdus if type(input_apdus) is list else [input_apdus])) + if len(self.input_apdus) == 0: + raise ValueError("Input is empty") + self.expected_response = bytes.fromhex(expected_response) + + def exchange(self, backend: BackendInterface) -> None: + for apdu in self.input_apdus[:-1]: + response = backend.exchange_raw(apdu) + assert response.status == 0x9000 + assert response.data == b"" + # Exchange last APDU and check the final result + response = backend.exchange_raw(self.input_apdus[-1]) + assert response.status == 0x9000 + assert response.data == self.expected_response + + @contextmanager + def exchange_async(self, backend: BackendInterface) -> Generator[None, None, None]: + for apdu in self.input_apdus[:-1]: + response = backend.exchange_raw(apdu) + assert response.status == 0x9000 + assert response.data == b"" + # Asynchronously exchange last APDU. The result can be checked with check_async_response + with backend.exchange_async_raw(self.input_apdus[-1]) as response: + yield response + + def check_async_response(self, backend: BackendInterface) -> None: + response = backend.last_async_response + assert response is not None + assert response.status == 0x9000 + assert response.data == self.expected_response diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000..88e9c51 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,4 @@ +pytest +ragger[speculos,ledgerwallet]>=1.11.4 +# ecdsa>=0.16.1,<0.17.0 +# pysha3>=1.0.0,<2.0.0 diff --git a/tests/setup.cfg b/tests/setup.cfg new file mode 100644 index 0000000..7d7ce5a --- /dev/null +++ b/tests/setup.cfg @@ -0,0 +1,21 @@ +[tool:pytest] +addopts = --strict-markers + +[pylint] +disable = C0114, # missing-module-docstring + C0115, # missing-class-docstring + C0116, # missing-function-docstring + C0103, # invalid-name + R0801, # duplicate-code + R0913 # too-many-arguments +max-line-length=120 +extension-pkg-whitelist=hid + +[pycodestyle] +max-line-length = 120 + +[mypy-hid.*] +ignore_missing_imports = True + +[mypy-pytest.*] +ignore_missing_imports = True diff --git a/tests/snapshots/flex/test_app_mainmenu/00000.png b/tests/snapshots/flex/test_app_mainmenu/00000.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_app_mainmenu/00000.png differ diff --git a/tests/snapshots/flex/test_app_mainmenu/00001.png b/tests/snapshots/flex/test_app_mainmenu/00001.png new file mode 100644 index 0000000..f39b025 Binary files /dev/null and b/tests/snapshots/flex/test_app_mainmenu/00001.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_approve/00000.png b/tests/snapshots/flex/test_get_public_key_confirm_approve/00000.png new file mode 100644 index 0000000..c0dd3bc Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_approve/00000.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_approve/00001.png b/tests/snapshots/flex/test_get_public_key_confirm_approve/00001.png new file mode 100644 index 0000000..b39fe00 Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_approve/00001.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_approve/00002.png b/tests/snapshots/flex/test_get_public_key_confirm_approve/00002.png new file mode 100644 index 0000000..1545889 Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_approve/00002.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_approve/00003.png b/tests/snapshots/flex/test_get_public_key_confirm_approve/00003.png new file mode 100644 index 0000000..b39fe00 Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_approve/00003.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_approve/00004.png b/tests/snapshots/flex/test_get_public_key_confirm_approve/00004.png new file mode 100644 index 0000000..4321e60 Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_approve/00004.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_approve/00005.png b/tests/snapshots/flex/test_get_public_key_confirm_approve/00005.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_approve/00005.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_0/00000.png b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_0/00000.png new file mode 100644 index 0000000..c0dd3bc Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_0/00000.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_0/00001.png b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_0/00001.png new file mode 100644 index 0000000..45c08d4 Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_0/00001.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_0/00002.png b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_0/00002.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_0/00002.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00000.png b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00000.png new file mode 100644 index 0000000..c0dd3bc Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00000.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00001.png b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00001.png new file mode 100644 index 0000000..b39fe00 Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00001.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00002.png b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00002.png new file mode 100644 index 0000000..45c08d4 Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00002.png differ diff --git a/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00003.png b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00003.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_get_public_key_confirm_reject/on_page_1/00003.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii/00000.png b/tests/snapshots/flex/test_sign_message_approve_ascii/00000.png new file mode 100644 index 0000000..d940532 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii/00000.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii/00001.png b/tests/snapshots/flex/test_sign_message_approve_ascii/00001.png new file mode 100644 index 0000000..39dfcea Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii/00001.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii/00002.png b/tests/snapshots/flex/test_sign_message_approve_ascii/00002.png new file mode 100644 index 0000000..4344cd5 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii/00002.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii/00003.png b/tests/snapshots/flex/test_sign_message_approve_ascii/00003.png new file mode 100644 index 0000000..8b981d4 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii/00003.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii/00004.png b/tests/snapshots/flex/test_sign_message_approve_ascii/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii/00004.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii_long/00000.png b/tests/snapshots/flex/test_sign_message_approve_ascii_long/00000.png new file mode 100644 index 0000000..d940532 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii_long/00000.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii_long/00001.png b/tests/snapshots/flex/test_sign_message_approve_ascii_long/00001.png new file mode 100644 index 0000000..c2b789b Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii_long/00001.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii_long/00002.png b/tests/snapshots/flex/test_sign_message_approve_ascii_long/00002.png new file mode 100644 index 0000000..4344cd5 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii_long/00002.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii_long/00003.png b/tests/snapshots/flex/test_sign_message_approve_ascii_long/00003.png new file mode 100644 index 0000000..8b981d4 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii_long/00003.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii_long/00004.png b/tests/snapshots/flex/test_sign_message_approve_ascii_long/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii_long/00004.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00000.png b/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00000.png new file mode 100644 index 0000000..c5cac46 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00000.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00001.png b/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00001.png new file mode 100644 index 0000000..43f1565 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00001.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00002.png b/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00002.png new file mode 100644 index 0000000..4344cd5 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00002.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00003.png b/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00003.png new file mode 100644 index 0000000..8b981d4 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00003.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00004.png b/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_ascii_overlong/00004.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_hex/00000.png b/tests/snapshots/flex/test_sign_message_approve_hex/00000.png new file mode 100644 index 0000000..c16dae6 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_hex/00000.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_hex/00001.png b/tests/snapshots/flex/test_sign_message_approve_hex/00001.png new file mode 100644 index 0000000..0c928f1 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_hex/00001.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_hex/00002.png b/tests/snapshots/flex/test_sign_message_approve_hex/00002.png new file mode 100644 index 0000000..4344cd5 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_hex/00002.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_hex/00003.png b/tests/snapshots/flex/test_sign_message_approve_hex/00003.png new file mode 100644 index 0000000..8b981d4 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_hex/00003.png differ diff --git a/tests/snapshots/flex/test_sign_message_approve_hex/00004.png b/tests/snapshots/flex/test_sign_message_approve_hex/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_approve_hex/00004.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_0/00000.png b/tests/snapshots/flex/test_sign_message_reject/on_page_0/00000.png new file mode 100644 index 0000000..d940532 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_0/00000.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_0/00001.png b/tests/snapshots/flex/test_sign_message_reject/on_page_0/00001.png new file mode 100644 index 0000000..2b40ba7 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_0/00001.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_0/00002.png b/tests/snapshots/flex/test_sign_message_reject/on_page_0/00002.png new file mode 100644 index 0000000..2d8bbe3 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_0/00002.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_0/00003.png b/tests/snapshots/flex/test_sign_message_reject/on_page_0/00003.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_0/00003.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_1/00000.png b/tests/snapshots/flex/test_sign_message_reject/on_page_1/00000.png new file mode 100644 index 0000000..d940532 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_1/00000.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_1/00001.png b/tests/snapshots/flex/test_sign_message_reject/on_page_1/00001.png new file mode 100644 index 0000000..39dfcea Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_1/00001.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_1/00002.png b/tests/snapshots/flex/test_sign_message_reject/on_page_1/00002.png new file mode 100644 index 0000000..2b40ba7 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_1/00002.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_1/00003.png b/tests/snapshots/flex/test_sign_message_reject/on_page_1/00003.png new file mode 100644 index 0000000..2d8bbe3 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_1/00003.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_1/00004.png b/tests/snapshots/flex/test_sign_message_reject/on_page_1/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_1/00004.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_2/00000.png b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00000.png new file mode 100644 index 0000000..d940532 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00000.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_2/00001.png b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00001.png new file mode 100644 index 0000000..39dfcea Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00001.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_2/00002.png b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00002.png new file mode 100644 index 0000000..4344cd5 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00002.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_2/00003.png b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00003.png new file mode 100644 index 0000000..2b40ba7 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00003.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_2/00004.png b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00004.png new file mode 100644 index 0000000..2d8bbe3 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00004.png differ diff --git a/tests/snapshots/flex/test_sign_message_reject/on_page_2/00005.png b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00005.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_message_reject/on_page_2/00005.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_basic/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_basic/00000.png new file mode 100644 index 0000000..d8baa06 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_basic/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_basic/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_basic/00001.png new file mode 100644 index 0000000..b8b1710 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_basic/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_basic/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_basic/00002.png new file mode 100644 index 0000000..a9b99e5 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_basic/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_basic/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_basic/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_basic/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_basic/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_basic/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_basic/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00000.png new file mode 100644 index 0000000..0362487 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00001.png new file mode 100644 index 0000000..e0aea6c Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00002.png new file mode 100644 index 0000000..3ab0152 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00003.png new file mode 100644 index 0000000..62ce8d9 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00004.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00005.png b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00005.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_basic_fee/00005.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00000.png new file mode 100644 index 0000000..0362487 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00001.png new file mode 100644 index 0000000..8cf7841 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00002.png new file mode 100644 index 0000000..3ab0152 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00003.png new file mode 100644 index 0000000..62ce8d9 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00004.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00005.png b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00005.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_ascii/00005.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00000.png new file mode 100644 index 0000000..0362487 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00001.png new file mode 100644 index 0000000..07ff564 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00002.png new file mode 100644 index 0000000..3ab0152 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00003.png new file mode 100644 index 0000000..62ce8d9 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00004.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00005.png b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00005.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_binary/00005.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00000.png new file mode 100644 index 0000000..3e1d960 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00001.png new file mode 100644 index 0000000..b8b1710 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00002.png new file mode 100644 index 0000000..79e6cfe Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_data_cashlink/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png new file mode 100644 index 0000000..ee50fc8 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png new file mode 100644 index 0000000..140087a Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png new file mode 100644 index 0000000..78fc5c7 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png new file mode 100644 index 0000000..ee50fc8 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png new file mode 100644 index 0000000..8e73e8c Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png new file mode 100644 index 0000000..78fc5c7 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00000.png new file mode 100644 index 0000000..e4cc9ab Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00001.png new file mode 100644 index 0000000..8e73e8c Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00002.png new file mode 100644 index 0000000..4b5cb9e Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00000.png new file mode 100644 index 0000000..e4cc9ab Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00001.png new file mode 100644 index 0000000..730eda7 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00002.png new file mode 100644 index 0000000..4b5cb9e Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_create_staker_delegation/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00000.png new file mode 100644 index 0000000..e12ee41 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00001.png new file mode 100644 index 0000000..6c1d1ae Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00002.png new file mode 100644 index 0000000..0594800 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_remove_stake/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00000.png new file mode 100644 index 0000000..7014150 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00001.png new file mode 100644 index 0000000..8e73e8c Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00002.png new file mode 100644 index 0000000..aa1bf26 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_retire_stake/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00000.png new file mode 100644 index 0000000..93e5d1b Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00001.png new file mode 100644 index 0000000..8e73e8c Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00002.png new file mode 100644 index 0000000..be9f56c Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_set_active_stake/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png new file mode 100644 index 0000000..a615963 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png new file mode 100644 index 0000000..9f71b8e Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png new file mode 100644 index 0000000..e66f07d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png new file mode 100644 index 0000000..a615963 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png new file mode 100644 index 0000000..2fa1a89 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png new file mode 100644 index 0000000..e66f07d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png new file mode 100644 index 0000000..be51a9d Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00000.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00000.png new file mode 100644 index 0000000..d8baa06 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00001.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00001.png new file mode 100644 index 0000000..6a11e11 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00002.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00002.png new file mode 100644 index 0000000..6bbdf2f Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00003.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00003.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_0/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00000.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00000.png new file mode 100644 index 0000000..d8baa06 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00001.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00001.png new file mode 100644 index 0000000..b8b1710 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00002.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00002.png new file mode 100644 index 0000000..6a11e11 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00003.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00003.png new file mode 100644 index 0000000..6bbdf2f Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00004.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00004.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_1/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00000.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00000.png new file mode 100644 index 0000000..d8baa06 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00000.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00001.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00001.png new file mode 100644 index 0000000..b8b1710 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00001.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00002.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00002.png new file mode 100644 index 0000000..a9b99e5 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00002.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00003.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00003.png new file mode 100644 index 0000000..6a11e11 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00003.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00004.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00004.png new file mode 100644 index 0000000..6bbdf2f Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00004.png differ diff --git a/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00005.png b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00005.png new file mode 100644 index 0000000..4165e86 Binary files /dev/null and b/tests/snapshots/flex/test_sign_transaction_reject/on_page_2/00005.png differ diff --git a/tests/snapshots/nanos/test_app_mainmenu/00000.png b/tests/snapshots/nanos/test_app_mainmenu/00000.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_app_mainmenu/00000.png differ diff --git a/tests/snapshots/nanos/test_app_mainmenu/00001.png b/tests/snapshots/nanos/test_app_mainmenu/00001.png new file mode 100644 index 0000000..625b9d3 Binary files /dev/null and b/tests/snapshots/nanos/test_app_mainmenu/00001.png differ diff --git a/tests/snapshots/nanos/test_app_mainmenu/00002.png b/tests/snapshots/nanos/test_app_mainmenu/00002.png new file mode 100644 index 0000000..e227980 Binary files /dev/null and b/tests/snapshots/nanos/test_app_mainmenu/00002.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_approve/00000.png b/tests/snapshots/nanos/test_get_public_key_confirm_approve/00000.png new file mode 100644 index 0000000..8654ba8 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_approve/00000.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_approve/00001.png b/tests/snapshots/nanos/test_get_public_key_confirm_approve/00001.png new file mode 100644 index 0000000..46b6b15 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_approve/00001.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_approve/00002.png b/tests/snapshots/nanos/test_get_public_key_confirm_approve/00002.png new file mode 100644 index 0000000..d6d0fe7 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_approve/00002.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_approve/00003.png b/tests/snapshots/nanos/test_get_public_key_confirm_approve/00003.png new file mode 100644 index 0000000..66c411c Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_approve/00003.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_approve/00004.png b/tests/snapshots/nanos/test_get_public_key_confirm_approve/00004.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_approve/00004.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_reject/00000.png b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00000.png new file mode 100644 index 0000000..8654ba8 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00000.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_reject/00001.png b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00001.png new file mode 100644 index 0000000..46b6b15 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00001.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_reject/00002.png b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00002.png new file mode 100644 index 0000000..d6d0fe7 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00002.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_reject/00003.png b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00003.png new file mode 100644 index 0000000..66c411c Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00003.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_reject/00004.png b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00004.png new file mode 100644 index 0000000..9c7e704 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00004.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_reject/00005.png b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00005.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_reject/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii/00000.png b/tests/snapshots/nanos/test_sign_message_approve_ascii/00000.png new file mode 100644 index 0000000..07ed09d Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii/00001.png b/tests/snapshots/nanos/test_sign_message_approve_ascii/00001.png new file mode 100644 index 0000000..56c708c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii/00002.png b/tests/snapshots/nanos/test_sign_message_approve_ascii/00002.png new file mode 100644 index 0000000..91e4b5b Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii/00003.png b/tests/snapshots/nanos/test_sign_message_approve_ascii/00003.png new file mode 100644 index 0000000..7a233bd Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii/00004.png b/tests/snapshots/nanos/test_sign_message_approve_ascii/00004.png new file mode 100644 index 0000000..f5c2d67 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii/00005.png b/tests/snapshots/nanos/test_sign_message_approve_ascii/00005.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00000.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00000.png new file mode 100644 index 0000000..8829212 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00001.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00001.png new file mode 100644 index 0000000..0203d90 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00002.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00002.png new file mode 100644 index 0000000..b550fb0 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00003.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00003.png new file mode 100644 index 0000000..718faed Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00004.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00004.png new file mode 100644 index 0000000..ff0a380 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00005.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00005.png new file mode 100644 index 0000000..b8f644e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00006.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00006.png new file mode 100644 index 0000000..b5b90f5 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00007.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00007.png new file mode 100644 index 0000000..e460c5a Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00008.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00008.png new file mode 100644 index 0000000..b2c1f5a Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00008.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00009.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00009.png new file mode 100644 index 0000000..f6fe643 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00009.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00010.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00010.png new file mode 100644 index 0000000..91e4b5b Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00010.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00011.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00011.png new file mode 100644 index 0000000..7a233bd Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00011.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00012.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00012.png new file mode 100644 index 0000000..f5c2d67 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00012.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00013.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00013.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_long/00013.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00000.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00000.png new file mode 100644 index 0000000..f047bfd Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00001.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00001.png new file mode 100644 index 0000000..201e82a Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00002.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00002.png new file mode 100644 index 0000000..bd3a022 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00003.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00003.png new file mode 100644 index 0000000..7705ce7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00004.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00004.png new file mode 100644 index 0000000..56b507f Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00005.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00005.png new file mode 100644 index 0000000..f5c2d67 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00006.png b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00006.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_ascii_overlong/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_hex/00000.png b/tests/snapshots/nanos/test_sign_message_approve_hex/00000.png new file mode 100644 index 0000000..a1fa02f Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_hex/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_hex/00001.png b/tests/snapshots/nanos/test_sign_message_approve_hex/00001.png new file mode 100644 index 0000000..1574d87 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_hex/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_hex/00002.png b/tests/snapshots/nanos/test_sign_message_approve_hex/00002.png new file mode 100644 index 0000000..7a233bd Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_hex/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_hex/00003.png b/tests/snapshots/nanos/test_sign_message_approve_hex/00003.png new file mode 100644 index 0000000..f5c2d67 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_hex/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_message_approve_hex/00004.png b/tests/snapshots/nanos/test_sign_message_approve_hex/00004.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_approve_hex/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_message_reject/00000.png b/tests/snapshots/nanos/test_sign_message_reject/00000.png new file mode 100644 index 0000000..ab16f62 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_reject/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_message_reject/00001.png b/tests/snapshots/nanos/test_sign_message_reject/00001.png new file mode 100644 index 0000000..07ed09d Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_reject/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_message_reject/00002.png b/tests/snapshots/nanos/test_sign_message_reject/00002.png new file mode 100644 index 0000000..56c708c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_reject/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_message_reject/00003.png b/tests/snapshots/nanos/test_sign_message_reject/00003.png new file mode 100644 index 0000000..91e4b5b Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_reject/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_message_reject/00004.png b/tests/snapshots/nanos/test_sign_message_reject/00004.png new file mode 100644 index 0000000..7a233bd Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_reject/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_message_reject/00005.png b/tests/snapshots/nanos/test_sign_message_reject/00005.png new file mode 100644 index 0000000..f5c2d67 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_reject/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_message_reject/00006.png b/tests/snapshots/nanos/test_sign_message_reject/00006.png new file mode 100644 index 0000000..9c7e704 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_reject/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_message_reject/00007.png b/tests/snapshots/nanos/test_sign_message_reject/00007.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_message_reject/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00000.png new file mode 100644 index 0000000..449e446 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00002.png new file mode 100644 index 0000000..14c0433 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00003.png new file mode 100644 index 0000000..1ef75cc Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00004.png new file mode 100644 index 0000000..a8f1e15 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic/00005.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00005.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic/00006.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00006.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic/00007.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00007.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00000.png new file mode 100644 index 0000000..449e446 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00002.png new file mode 100644 index 0000000..14c0433 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00003.png new file mode 100644 index 0000000..1ef75cc Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00004.png new file mode 100644 index 0000000..a8f1e15 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00005.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00005.png new file mode 100644 index 0000000..89b639b Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00006.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00006.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00007.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00007.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00008.png b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00008.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_basic_fee/00008.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00000.png new file mode 100644 index 0000000..449e446 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00002.png new file mode 100644 index 0000000..14c0433 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00003.png new file mode 100644 index 0000000..1ef75cc Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00004.png new file mode 100644 index 0000000..a8f1e15 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00005.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00005.png new file mode 100644 index 0000000..42d6987 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00006.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00006.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00007.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00007.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00008.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00008.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_ascii/00008.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00000.png new file mode 100644 index 0000000..449e446 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00002.png new file mode 100644 index 0000000..14c0433 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00003.png new file mode 100644 index 0000000..1ef75cc Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00004.png new file mode 100644 index 0000000..a8f1e15 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00005.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00005.png new file mode 100644 index 0000000..6864365 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00006.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00006.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00007.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00007.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00008.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00008.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_binary/00008.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00000.png new file mode 100644 index 0000000..fe327b7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00002.png new file mode 100644 index 0000000..14c0433 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00003.png new file mode 100644 index 0000000..1ef75cc Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00004.png new file mode 100644 index 0000000..a8f1e15 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00005.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00005.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00006.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00006.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00007.png b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00007.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_data_cashlink/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png new file mode 100644 index 0000000..b54bed2 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png new file mode 100644 index 0000000..e43b79b Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png new file mode 100644 index 0000000..28e998d Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png new file mode 100644 index 0000000..f5fd798 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00005.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00005.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00006.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00006.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00007.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00007.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_different_staker/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png new file mode 100644 index 0000000..b54bed2 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00000.png new file mode 100644 index 0000000..451ae78 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00002.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00003.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00004.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00000.png new file mode 100644 index 0000000..451ae78 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00002.png new file mode 100644 index 0000000..2b5adec Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00003.png new file mode 100644 index 0000000..3451af6 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00004.png new file mode 100644 index 0000000..d0ded54 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00005.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00005.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00006.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00006.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00007.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00007.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_create_staker_delegation/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00000.png new file mode 100644 index 0000000..75d7de9 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00002.png new file mode 100644 index 0000000..5b3ce7c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00003.png new file mode 100644 index 0000000..8ead47a Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00004.png new file mode 100644 index 0000000..b2201b3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00005.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00005.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00006.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00006.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00007.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00007.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_remove_stake/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00000.png new file mode 100644 index 0000000..24b0c25 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00002.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00003.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00004.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_retire_stake/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00000.png new file mode 100644 index 0000000..b068451 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00002.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00003.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00004.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_set_active_stake/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png new file mode 100644 index 0000000..2c6364f Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png new file mode 100644 index 0000000..91678c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png new file mode 100644 index 0000000..2c6364f Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png new file mode 100644 index 0000000..2b5adec Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png new file mode 100644 index 0000000..3451af6 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png new file mode 100644 index 0000000..d0ded54 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png new file mode 100644 index 0000000..e7c8b4c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00005.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00005.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00006.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00006.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00007.png b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00007.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_reject/00000.png b/tests/snapshots/nanos/test_sign_transaction_reject/00000.png new file mode 100644 index 0000000..449e446 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_reject/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_reject/00001.png b/tests/snapshots/nanos/test_sign_transaction_reject/00001.png new file mode 100644 index 0000000..621b48e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_reject/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_reject/00002.png b/tests/snapshots/nanos/test_sign_transaction_reject/00002.png new file mode 100644 index 0000000..14c0433 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_reject/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_reject/00003.png b/tests/snapshots/nanos/test_sign_transaction_reject/00003.png new file mode 100644 index 0000000..1ef75cc Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_reject/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_reject/00004.png b/tests/snapshots/nanos/test_sign_transaction_reject/00004.png new file mode 100644 index 0000000..a8f1e15 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_reject/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_reject/00005.png b/tests/snapshots/nanos/test_sign_transaction_reject/00005.png new file mode 100644 index 0000000..c8b36c7 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_reject/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_reject/00006.png b/tests/snapshots/nanos/test_sign_transaction_reject/00006.png new file mode 100644 index 0000000..1c9156c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_reject/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_reject/00007.png b/tests/snapshots/nanos/test_sign_transaction_reject/00007.png new file mode 100644 index 0000000..9c7e704 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_reject/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_transaction_reject/00008.png b/tests/snapshots/nanos/test_sign_transaction_reject/00008.png new file mode 100644 index 0000000..ce795f3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_transaction_reject/00008.png differ diff --git a/tests/snapshots/nanosp/test_app_mainmenu/00000.png b/tests/snapshots/nanosp/test_app_mainmenu/00000.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_app_mainmenu/00000.png differ diff --git a/tests/snapshots/nanosp/test_app_mainmenu/00001.png b/tests/snapshots/nanosp/test_app_mainmenu/00001.png new file mode 100644 index 0000000..472505b Binary files /dev/null and b/tests/snapshots/nanosp/test_app_mainmenu/00001.png differ diff --git a/tests/snapshots/nanosp/test_app_mainmenu/00002.png b/tests/snapshots/nanosp/test_app_mainmenu/00002.png new file mode 100644 index 0000000..bcb20c6 Binary files /dev/null and b/tests/snapshots/nanosp/test_app_mainmenu/00002.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_approve/00000.png b/tests/snapshots/nanosp/test_get_public_key_confirm_approve/00000.png new file mode 100644 index 0000000..b618fcd Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_approve/00000.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_approve/00001.png b/tests/snapshots/nanosp/test_get_public_key_confirm_approve/00001.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_approve/00001.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_approve/00002.png b/tests/snapshots/nanosp/test_get_public_key_confirm_approve/00002.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_approve/00002.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00000.png b/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00000.png new file mode 100644 index 0000000..b618fcd Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00000.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00001.png b/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00001.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00001.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00002.png b/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00002.png new file mode 100644 index 0000000..e90cd9d Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00002.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00003.png b/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00003.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_reject/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii/00000.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00000.png new file mode 100644 index 0000000..8235217 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii/00001.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00001.png new file mode 100644 index 0000000..f419fb8 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii/00002.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00002.png new file mode 100644 index 0000000..57fe479 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii/00003.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00003.png new file mode 100644 index 0000000..fcf33d1 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii/00004.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00004.png new file mode 100644 index 0000000..e7ffedd Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii/00005.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00000.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00000.png new file mode 100644 index 0000000..391b4fe Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00001.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00001.png new file mode 100644 index 0000000..22bdb80 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00002.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00002.png new file mode 100644 index 0000000..70fe051 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00003.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00003.png new file mode 100644 index 0000000..2c0812d Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00004.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00004.png new file mode 100644 index 0000000..57fe479 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00005.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00005.png new file mode 100644 index 0000000..fcf33d1 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00006.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00006.png new file mode 100644 index 0000000..e7ffedd Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00006.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00007.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00007.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_long/00007.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00000.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00000.png new file mode 100644 index 0000000..96d61cd Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00001.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00001.png new file mode 100644 index 0000000..4fbd2bb Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00002.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00002.png new file mode 100644 index 0000000..fc480f1 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00003.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00003.png new file mode 100644 index 0000000..e7ffedd Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00004.png b/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_ascii_overlong/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_hex/00000.png b/tests/snapshots/nanosp/test_sign_message_approve_hex/00000.png new file mode 100644 index 0000000..5cc2a1c Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_hex/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_hex/00001.png b/tests/snapshots/nanosp/test_sign_message_approve_hex/00001.png new file mode 100644 index 0000000..9ac9d42 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_hex/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_hex/00002.png b/tests/snapshots/nanosp/test_sign_message_approve_hex/00002.png new file mode 100644 index 0000000..fcf33d1 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_hex/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_hex/00003.png b/tests/snapshots/nanosp/test_sign_message_approve_hex/00003.png new file mode 100644 index 0000000..e7ffedd Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_hex/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_approve_hex/00004.png b/tests/snapshots/nanosp/test_sign_message_approve_hex/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_approve_hex/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_reject/00000.png b/tests/snapshots/nanosp/test_sign_message_reject/00000.png new file mode 100644 index 0000000..1b27154 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_reject/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_reject/00001.png b/tests/snapshots/nanosp/test_sign_message_reject/00001.png new file mode 100644 index 0000000..8235217 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_reject/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_reject/00002.png b/tests/snapshots/nanosp/test_sign_message_reject/00002.png new file mode 100644 index 0000000..f419fb8 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_reject/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_reject/00003.png b/tests/snapshots/nanosp/test_sign_message_reject/00003.png new file mode 100644 index 0000000..57fe479 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_reject/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_reject/00004.png b/tests/snapshots/nanosp/test_sign_message_reject/00004.png new file mode 100644 index 0000000..fcf33d1 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_reject/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_reject/00005.png b/tests/snapshots/nanosp/test_sign_message_reject/00005.png new file mode 100644 index 0000000..e7ffedd Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_reject/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_reject/00006.png b/tests/snapshots/nanosp/test_sign_message_reject/00006.png new file mode 100644 index 0000000..e90cd9d Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_reject/00006.png differ diff --git a/tests/snapshots/nanosp/test_sign_message_reject/00007.png b/tests/snapshots/nanosp/test_sign_message_reject/00007.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_message_reject/00007.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00000.png new file mode 100644 index 0000000..ef59688 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00005.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00000.png new file mode 100644 index 0000000..ef59688 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00003.png new file mode 100644 index 0000000..eff586e Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00004.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00005.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00005.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00006.png b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00006.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_basic_fee/00006.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00000.png new file mode 100644 index 0000000..ef59688 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00003.png new file mode 100644 index 0000000..a20c4fe Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00004.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00005.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00005.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00006.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00006.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_ascii/00006.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00000.png new file mode 100644 index 0000000..ef59688 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00003.png new file mode 100644 index 0000000..5d3359f Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00004.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00005.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00005.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00006.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00006.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_binary/00006.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00000.png new file mode 100644 index 0000000..a848238 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00005.png b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_data_cashlink/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png new file mode 100644 index 0000000..88ff3c4 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png new file mode 100644 index 0000000..1d14fbf Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00005.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_different_staker/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png new file mode 100644 index 0000000..88ff3c4 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00000.png new file mode 100644 index 0000000..24d9034 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00002.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00003.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00000.png new file mode 100644 index 0000000..24d9034 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00002.png new file mode 100644 index 0000000..5a76723 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00005.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_create_staker_delegation/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00000.png new file mode 100644 index 0000000..e9c709d Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00002.png new file mode 100644 index 0000000..10e0fbb Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00005.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_remove_stake/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00000.png new file mode 100644 index 0000000..2e14df0 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00002.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00003.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_retire_stake/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00000.png new file mode 100644 index 0000000..cd919c7 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00002.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00003.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_set_active_stake/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png new file mode 100644 index 0000000..892c47e Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png new file mode 100644 index 0000000..bdb7ff7 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png new file mode 100644 index 0000000..892c47e Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png new file mode 100644 index 0000000..5a76723 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png new file mode 100644 index 0000000..624cbcc Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00005.png b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_reject/00000.png b/tests/snapshots/nanosp/test_sign_transaction_reject/00000.png new file mode 100644 index 0000000..ef59688 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_reject/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_reject/00001.png b/tests/snapshots/nanosp/test_sign_transaction_reject/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_reject/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_reject/00002.png b/tests/snapshots/nanosp/test_sign_transaction_reject/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_reject/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_reject/00003.png b/tests/snapshots/nanosp/test_sign_transaction_reject/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_reject/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_reject/00004.png b/tests/snapshots/nanosp/test_sign_transaction_reject/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_reject/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_reject/00005.png b/tests/snapshots/nanosp/test_sign_transaction_reject/00005.png new file mode 100644 index 0000000..e90cd9d Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_reject/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_transaction_reject/00006.png b/tests/snapshots/nanosp/test_sign_transaction_reject/00006.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_transaction_reject/00006.png differ diff --git a/tests/snapshots/nanox/test_app_mainmenu/00000.png b/tests/snapshots/nanox/test_app_mainmenu/00000.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_app_mainmenu/00000.png differ diff --git a/tests/snapshots/nanox/test_app_mainmenu/00001.png b/tests/snapshots/nanox/test_app_mainmenu/00001.png new file mode 100644 index 0000000..472505b Binary files /dev/null and b/tests/snapshots/nanox/test_app_mainmenu/00001.png differ diff --git a/tests/snapshots/nanox/test_app_mainmenu/00002.png b/tests/snapshots/nanox/test_app_mainmenu/00002.png new file mode 100644 index 0000000..bcb20c6 Binary files /dev/null and b/tests/snapshots/nanox/test_app_mainmenu/00002.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_approve/00000.png b/tests/snapshots/nanox/test_get_public_key_confirm_approve/00000.png new file mode 100644 index 0000000..b618fcd Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_approve/00000.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_approve/00001.png b/tests/snapshots/nanox/test_get_public_key_confirm_approve/00001.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_approve/00001.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_approve/00002.png b/tests/snapshots/nanox/test_get_public_key_confirm_approve/00002.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_approve/00002.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_reject/00000.png b/tests/snapshots/nanox/test_get_public_key_confirm_reject/00000.png new file mode 100644 index 0000000..b618fcd Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_reject/00000.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_reject/00001.png b/tests/snapshots/nanox/test_get_public_key_confirm_reject/00001.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_reject/00001.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_reject/00002.png b/tests/snapshots/nanox/test_get_public_key_confirm_reject/00002.png new file mode 100644 index 0000000..e90cd9d Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_reject/00002.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_reject/00003.png b/tests/snapshots/nanox/test_get_public_key_confirm_reject/00003.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_reject/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii/00000.png b/tests/snapshots/nanox/test_sign_message_approve_ascii/00000.png new file mode 100644 index 0000000..8235217 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii/00001.png b/tests/snapshots/nanox/test_sign_message_approve_ascii/00001.png new file mode 100644 index 0000000..f419fb8 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii/00002.png b/tests/snapshots/nanox/test_sign_message_approve_ascii/00002.png new file mode 100644 index 0000000..57fe479 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii/00003.png b/tests/snapshots/nanox/test_sign_message_approve_ascii/00003.png new file mode 100644 index 0000000..fcf33d1 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii/00004.png b/tests/snapshots/nanox/test_sign_message_approve_ascii/00004.png new file mode 100644 index 0000000..e7ffedd Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii/00005.png b/tests/snapshots/nanox/test_sign_message_approve_ascii/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00000.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00000.png new file mode 100644 index 0000000..391b4fe Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00001.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00001.png new file mode 100644 index 0000000..22bdb80 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00002.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00002.png new file mode 100644 index 0000000..70fe051 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00003.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00003.png new file mode 100644 index 0000000..2c0812d Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00004.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00004.png new file mode 100644 index 0000000..57fe479 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00005.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00005.png new file mode 100644 index 0000000..fcf33d1 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00006.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00006.png new file mode 100644 index 0000000..e7ffedd Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00006.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00007.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00007.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_long/00007.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00000.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00000.png new file mode 100644 index 0000000..96d61cd Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00001.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00001.png new file mode 100644 index 0000000..4fbd2bb Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00002.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00002.png new file mode 100644 index 0000000..fc480f1 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00003.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00003.png new file mode 100644 index 0000000..e7ffedd Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00004.png b/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_ascii_overlong/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_hex/00000.png b/tests/snapshots/nanox/test_sign_message_approve_hex/00000.png new file mode 100644 index 0000000..5cc2a1c Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_hex/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_hex/00001.png b/tests/snapshots/nanox/test_sign_message_approve_hex/00001.png new file mode 100644 index 0000000..9ac9d42 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_hex/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_hex/00002.png b/tests/snapshots/nanox/test_sign_message_approve_hex/00002.png new file mode 100644 index 0000000..fcf33d1 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_hex/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_hex/00003.png b/tests/snapshots/nanox/test_sign_message_approve_hex/00003.png new file mode 100644 index 0000000..e7ffedd Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_hex/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_message_approve_hex/00004.png b/tests/snapshots/nanox/test_sign_message_approve_hex/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_approve_hex/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_message_reject/00000.png b/tests/snapshots/nanox/test_sign_message_reject/00000.png new file mode 100644 index 0000000..1b27154 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_reject/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_message_reject/00001.png b/tests/snapshots/nanox/test_sign_message_reject/00001.png new file mode 100644 index 0000000..8235217 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_reject/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_message_reject/00002.png b/tests/snapshots/nanox/test_sign_message_reject/00002.png new file mode 100644 index 0000000..f419fb8 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_reject/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_message_reject/00003.png b/tests/snapshots/nanox/test_sign_message_reject/00003.png new file mode 100644 index 0000000..57fe479 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_reject/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_message_reject/00004.png b/tests/snapshots/nanox/test_sign_message_reject/00004.png new file mode 100644 index 0000000..fcf33d1 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_reject/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_message_reject/00005.png b/tests/snapshots/nanox/test_sign_message_reject/00005.png new file mode 100644 index 0000000..e7ffedd Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_reject/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_message_reject/00006.png b/tests/snapshots/nanox/test_sign_message_reject/00006.png new file mode 100644 index 0000000..e90cd9d Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_reject/00006.png differ diff --git a/tests/snapshots/nanox/test_sign_message_reject/00007.png b/tests/snapshots/nanox/test_sign_message_reject/00007.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_message_reject/00007.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00000.png new file mode 100644 index 0000000..ef59688 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic/00005.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00000.png new file mode 100644 index 0000000..ef59688 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00003.png new file mode 100644 index 0000000..eff586e Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00004.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00005.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00005.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00006.png b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00006.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_basic_fee/00006.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00000.png new file mode 100644 index 0000000..ef59688 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00003.png new file mode 100644 index 0000000..a20c4fe Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00004.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00005.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00005.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00006.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00006.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_ascii/00006.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00000.png new file mode 100644 index 0000000..ef59688 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00003.png new file mode 100644 index 0000000..5d3359f Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00004.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00005.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00005.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00006.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00006.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_binary/00006.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00000.png new file mode 100644 index 0000000..a848238 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00005.png b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_data_cashlink/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png new file mode 100644 index 0000000..88ff3c4 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png new file mode 100644 index 0000000..1d14fbf Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00005.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_different_staker/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png new file mode 100644 index 0000000..88ff3c4 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00000.png new file mode 100644 index 0000000..24d9034 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00002.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00003.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00000.png new file mode 100644 index 0000000..24d9034 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00002.png new file mode 100644 index 0000000..5a76723 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00005.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_create_staker_delegation/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00000.png new file mode 100644 index 0000000..e9c709d Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00002.png new file mode 100644 index 0000000..10e0fbb Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00005.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_remove_stake/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00000.png new file mode 100644 index 0000000..2e14df0 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00002.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00003.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_retire_stake/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00000.png new file mode 100644 index 0000000..cd919c7 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00002.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00003.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_set_active_stake/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png new file mode 100644 index 0000000..892c47e Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png new file mode 100644 index 0000000..bdb7ff7 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png new file mode 100644 index 0000000..892c47e Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png new file mode 100644 index 0000000..5a76723 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png new file mode 100644 index 0000000..624cbcc Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00005.png b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00005.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_reject/00000.png b/tests/snapshots/nanox/test_sign_transaction_reject/00000.png new file mode 100644 index 0000000..ef59688 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_reject/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_reject/00001.png b/tests/snapshots/nanox/test_sign_transaction_reject/00001.png new file mode 100644 index 0000000..9827ed5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_reject/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_reject/00002.png b/tests/snapshots/nanox/test_sign_transaction_reject/00002.png new file mode 100644 index 0000000..9cefe0c Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_reject/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_reject/00003.png b/tests/snapshots/nanox/test_sign_transaction_reject/00003.png new file mode 100644 index 0000000..9c244d9 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_reject/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_reject/00004.png b/tests/snapshots/nanox/test_sign_transaction_reject/00004.png new file mode 100644 index 0000000..570ce28 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_reject/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_reject/00005.png b/tests/snapshots/nanox/test_sign_transaction_reject/00005.png new file mode 100644 index 0000000..e90cd9d Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_reject/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_transaction_reject/00006.png b/tests/snapshots/nanox/test_sign_transaction_reject/00006.png new file mode 100644 index 0000000..6578872 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_transaction_reject/00006.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00000.png b/tests/snapshots/stax/test_app_mainmenu/00000.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_app_mainmenu/00000.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00001.png b/tests/snapshots/stax/test_app_mainmenu/00001.png new file mode 100644 index 0000000..679095f Binary files /dev/null and b/tests/snapshots/stax/test_app_mainmenu/00001.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_approve/00000.png b/tests/snapshots/stax/test_get_public_key_confirm_approve/00000.png new file mode 100644 index 0000000..ebe07f3 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_approve/00000.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_approve/00001.png b/tests/snapshots/stax/test_get_public_key_confirm_approve/00001.png new file mode 100644 index 0000000..7cec798 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_approve/00001.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_approve/00002.png b/tests/snapshots/stax/test_get_public_key_confirm_approve/00002.png new file mode 100644 index 0000000..ea000df Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_approve/00002.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_approve/00003.png b/tests/snapshots/stax/test_get_public_key_confirm_approve/00003.png new file mode 100644 index 0000000..7cec798 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_approve/00003.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_approve/00004.png b/tests/snapshots/stax/test_get_public_key_confirm_approve/00004.png new file mode 100644 index 0000000..7a49478 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_approve/00004.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_approve/00005.png b/tests/snapshots/stax/test_get_public_key_confirm_approve/00005.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_approve/00005.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_0/00000.png b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_0/00000.png new file mode 100644 index 0000000..ebe07f3 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_0/00000.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_0/00001.png b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_0/00001.png new file mode 100644 index 0000000..94c91bb Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_0/00001.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_0/00002.png b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_0/00002.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_0/00002.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00000.png b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00000.png new file mode 100644 index 0000000..ebe07f3 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00000.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00001.png b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00001.png new file mode 100644 index 0000000..7cec798 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00001.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00002.png b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00002.png new file mode 100644 index 0000000..94c91bb Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00002.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00003.png b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00003.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_reject/on_page_1/00003.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii/00000.png b/tests/snapshots/stax/test_sign_message_approve_ascii/00000.png new file mode 100644 index 0000000..2ba044d Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii/00000.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii/00001.png b/tests/snapshots/stax/test_sign_message_approve_ascii/00001.png new file mode 100644 index 0000000..1130555 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii/00001.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii/00002.png b/tests/snapshots/stax/test_sign_message_approve_ascii/00002.png new file mode 100644 index 0000000..92ca9cc Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii/00002.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii/00003.png b/tests/snapshots/stax/test_sign_message_approve_ascii/00003.png new file mode 100644 index 0000000..cfee3ae Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii/00003.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii/00004.png b/tests/snapshots/stax/test_sign_message_approve_ascii/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii/00004.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii_long/00000.png b/tests/snapshots/stax/test_sign_message_approve_ascii_long/00000.png new file mode 100644 index 0000000..2ba044d Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii_long/00000.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii_long/00001.png b/tests/snapshots/stax/test_sign_message_approve_ascii_long/00001.png new file mode 100644 index 0000000..7f7483b Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii_long/00001.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii_long/00002.png b/tests/snapshots/stax/test_sign_message_approve_ascii_long/00002.png new file mode 100644 index 0000000..92ca9cc Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii_long/00002.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii_long/00003.png b/tests/snapshots/stax/test_sign_message_approve_ascii_long/00003.png new file mode 100644 index 0000000..cfee3ae Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii_long/00003.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii_long/00004.png b/tests/snapshots/stax/test_sign_message_approve_ascii_long/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii_long/00004.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00000.png b/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00000.png new file mode 100644 index 0000000..faf9ccd Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00000.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00001.png b/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00001.png new file mode 100644 index 0000000..76c824b Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00001.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00002.png b/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00002.png new file mode 100644 index 0000000..92ca9cc Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00002.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00003.png b/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00003.png new file mode 100644 index 0000000..cfee3ae Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00003.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00004.png b/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_ascii_overlong/00004.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_hex/00000.png b/tests/snapshots/stax/test_sign_message_approve_hex/00000.png new file mode 100644 index 0000000..64deb1f Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_hex/00000.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_hex/00001.png b/tests/snapshots/stax/test_sign_message_approve_hex/00001.png new file mode 100644 index 0000000..b3c71de Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_hex/00001.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_hex/00002.png b/tests/snapshots/stax/test_sign_message_approve_hex/00002.png new file mode 100644 index 0000000..92ca9cc Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_hex/00002.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_hex/00003.png b/tests/snapshots/stax/test_sign_message_approve_hex/00003.png new file mode 100644 index 0000000..cfee3ae Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_hex/00003.png differ diff --git a/tests/snapshots/stax/test_sign_message_approve_hex/00004.png b/tests/snapshots/stax/test_sign_message_approve_hex/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_approve_hex/00004.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_0/00000.png b/tests/snapshots/stax/test_sign_message_reject/on_page_0/00000.png new file mode 100644 index 0000000..2ba044d Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_0/00000.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_0/00001.png b/tests/snapshots/stax/test_sign_message_reject/on_page_0/00001.png new file mode 100644 index 0000000..45414d5 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_0/00001.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_0/00002.png b/tests/snapshots/stax/test_sign_message_reject/on_page_0/00002.png new file mode 100644 index 0000000..fecace7 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_0/00002.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_0/00003.png b/tests/snapshots/stax/test_sign_message_reject/on_page_0/00003.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_0/00003.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_1/00000.png b/tests/snapshots/stax/test_sign_message_reject/on_page_1/00000.png new file mode 100644 index 0000000..2ba044d Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_1/00000.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_1/00001.png b/tests/snapshots/stax/test_sign_message_reject/on_page_1/00001.png new file mode 100644 index 0000000..1130555 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_1/00001.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_1/00002.png b/tests/snapshots/stax/test_sign_message_reject/on_page_1/00002.png new file mode 100644 index 0000000..45414d5 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_1/00002.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_1/00003.png b/tests/snapshots/stax/test_sign_message_reject/on_page_1/00003.png new file mode 100644 index 0000000..fecace7 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_1/00003.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_1/00004.png b/tests/snapshots/stax/test_sign_message_reject/on_page_1/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_1/00004.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_2/00000.png b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00000.png new file mode 100644 index 0000000..2ba044d Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00000.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_2/00001.png b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00001.png new file mode 100644 index 0000000..1130555 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00001.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_2/00002.png b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00002.png new file mode 100644 index 0000000..92ca9cc Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00002.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_2/00003.png b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00003.png new file mode 100644 index 0000000..45414d5 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00003.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_2/00004.png b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00004.png new file mode 100644 index 0000000..fecace7 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00004.png differ diff --git a/tests/snapshots/stax/test_sign_message_reject/on_page_2/00005.png b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00005.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_message_reject/on_page_2/00005.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_basic/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_basic/00000.png new file mode 100644 index 0000000..c185dc8 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_basic/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_basic/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_basic/00001.png new file mode 100644 index 0000000..a053dd7 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_basic/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_basic/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_basic/00002.png new file mode 100644 index 0000000..92e0b33 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_basic/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_basic/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_basic/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_basic/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_basic/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_basic/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_basic/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00000.png new file mode 100644 index 0000000..c185dc8 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00001.png new file mode 100644 index 0000000..55aeed8 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00002.png new file mode 100644 index 0000000..92e0b33 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_basic_fee/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00000.png new file mode 100644 index 0000000..c185dc8 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00001.png new file mode 100644 index 0000000..1319ffa Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00002.png new file mode 100644 index 0000000..92e0b33 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_ascii/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00000.png new file mode 100644 index 0000000..c185dc8 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00001.png new file mode 100644 index 0000000..7b77fb0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00002.png new file mode 100644 index 0000000..92e0b33 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_binary/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00000.png new file mode 100644 index 0000000..028f60b Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00001.png new file mode 100644 index 0000000..a053dd7 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00002.png new file mode 100644 index 0000000..4a321c1 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_data_cashlink/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png new file mode 100644 index 0000000..2a20dd2 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png new file mode 100644 index 0000000..f07d215 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png new file mode 100644 index 0000000..3e1da46 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_different_staker/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png new file mode 100644 index 0000000..2a20dd2 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png new file mode 100644 index 0000000..67400cd Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png new file mode 100644 index 0000000..3e1da46 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_add_stake_sender_staker/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00000.png new file mode 100644 index 0000000..d15ad60 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00001.png new file mode 100644 index 0000000..67400cd Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00002.png new file mode 100644 index 0000000..dac8232 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00000.png new file mode 100644 index 0000000..d15ad60 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00001.png new file mode 100644 index 0000000..14b6e4d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00002.png new file mode 100644 index 0000000..dac8232 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_create_staker_delegation/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00000.png new file mode 100644 index 0000000..33dae03 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00001.png new file mode 100644 index 0000000..761ed59 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00002.png new file mode 100644 index 0000000..9cac669 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_remove_stake/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00000.png new file mode 100644 index 0000000..51a89b4 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00001.png new file mode 100644 index 0000000..67400cd Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00002.png new file mode 100644 index 0000000..3939876 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_retire_stake/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00000.png new file mode 100644 index 0000000..5824b32 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00001.png new file mode 100644 index 0000000..67400cd Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00002.png new file mode 100644 index 0000000..d3be255 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_set_active_stake/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png new file mode 100644 index 0000000..d8832ea Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png new file mode 100644 index 0000000..fab85b4 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png new file mode 100644 index 0000000..202efb8 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_no_new_delegation_and_with_reactivate/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png new file mode 100644 index 0000000..d8832ea Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png new file mode 100644 index 0000000..ee9ad29 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png new file mode 100644 index 0000000..202efb8 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png new file mode 100644 index 0000000..392165d Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_approve_staking_update_staker_with_new_delegation_and_no_reactivate/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00000.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00000.png new file mode 100644 index 0000000..c185dc8 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00001.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00001.png new file mode 100644 index 0000000..abc9677 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00002.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00002.png new file mode 100644 index 0000000..2b66970 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00003.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00003.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_0/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00000.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00000.png new file mode 100644 index 0000000..c185dc8 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00001.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00001.png new file mode 100644 index 0000000..a053dd7 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00002.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00002.png new file mode 100644 index 0000000..abc9677 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00003.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00003.png new file mode 100644 index 0000000..2b66970 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00004.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00004.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_1/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00000.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00000.png new file mode 100644 index 0000000..c185dc8 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00000.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00001.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00001.png new file mode 100644 index 0000000..a053dd7 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00001.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00002.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00002.png new file mode 100644 index 0000000..92e0b33 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00002.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00003.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00003.png new file mode 100644 index 0000000..abc9677 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00003.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00004.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00004.png new file mode 100644 index 0000000..2b66970 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00004.png differ diff --git a/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00005.png b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00005.png new file mode 100644 index 0000000..a91b2a0 Binary files /dev/null and b/tests/snapshots/stax/test_sign_transaction_reject/on_page_2/00005.png differ diff --git a/tests/test_app_mainmenu.py b/tests/test_app_mainmenu.py new file mode 100644 index 0000000..1389698 --- /dev/null +++ b/tests/test_app_mainmenu.py @@ -0,0 +1,19 @@ +from ragger.navigator import NavInsID, NavIns + +def test_app_mainmenu(firmware, navigator, test_name, default_screenshot_path): + # Navigate in the main menu + if firmware.is_nano: + instructions = [ + NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK, + ] + else: + instructions = [ + NavInsID.USE_CASE_HOME_INFO, + ] + navigator.navigate_and_compare( + default_screenshot_path, + test_name, + instructions, + screen_change_before_first_instruction = False, + ) diff --git a/tests/test_get_name_and_version.py b/tests/test_get_name_and_version.py new file mode 100644 index 0000000..c0da2b1 --- /dev/null +++ b/tests/test_get_name_and_version.py @@ -0,0 +1,29 @@ +from utils import pop_sized_buf_from_buffer, pop_size_prefixed_buf_from_buf + +def test_get_app_and_version(backend): + # Note that this is a request handled by BOLOS, see io_process_default_apdus in ledger-secure-sdk. + response = backend.exchange( + cla=0xB0, # specific CLA for BOLOS + ins=0x01, # specific INS for get_app_and_version + p1=0x00, + p2=0x00, + data=b"", + ).data + # response = format_id (1) + # app_name_raw_len (1) + # app_name_raw (var) + # version_raw_len (1) + # version_raw (var) + # unused_len (1) + # unused (var) + response, _ = pop_sized_buf_from_buffer(response, 1) + response, _, app_name_raw = pop_size_prefixed_buf_from_buf(response) + response, _, version_raw = pop_size_prefixed_buf_from_buf(response) + response, _, _ = pop_size_prefixed_buf_from_buf(response) + + assert len(response) == 0 + + app_name, version = app_name_raw.decode("ascii"), version_raw.decode("ascii") + + assert app_name == "Nimiq" + assert version == "2.0.0" diff --git a/tests/test_get_public_key.py b/tests/test_get_public_key.py new file mode 100644 index 0000000..e5bf9be --- /dev/null +++ b/tests/test_get_public_key.py @@ -0,0 +1,80 @@ +import pytest +from ragger.error import ExceptionRAPDU +from ragger.navigator import NavInsID, NavIns + +from raw_apdu_exchange import RawApduExchange +from errors import Errors + +APDUS = { + "no_confirm": RawApduExchange( + "e002000011048000002c800000f28000000080000000", # confirm flag unset + "6b20f8ca4ed445c2d666e80361cbc1b98d0d55ca44ad8c560755d528f8d3d71c", + ), + "confirm": RawApduExchange( + "e002000111048000002c800000f28000000080000000", # confirm flag set + "6b20f8ca4ed445c2d666e80361cbc1b98d0d55ca44ad8c560755d528f8d3d71c" + ), +} + +def test_get_public_key_no_confirm(backend): + APDUS["no_confirm"].exchange(backend) + +def test_get_public_key_confirm_approve(firmware, backend, navigator, default_screenshot_path, test_name): + with APDUS["confirm"].exchange_async(backend): + if firmware.device.startswith("nano"): + navigator.navigate_until_text_and_compare( + NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Approve", + default_screenshot_path, + test_name, + ) + else: + instructions = [ + NavInsID.USE_CASE_REVIEW_TAP, + NavIns(NavInsID.TOUCH, (64, 520) if firmware.device.startswith("stax") else (80, 435)), # QR button + NavInsID.USE_CASE_ADDRESS_CONFIRMATION_EXIT_QR, + NavInsID.USE_CASE_ADDRESS_CONFIRMATION_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS + ] + navigator.navigate_and_compare( + default_screenshot_path, + test_name, + instructions, + ) + APDUS["confirm"].check_async_response(backend) + +def test_get_public_key_confirm_reject(firmware, backend, navigator, default_screenshot_path, test_name): + if firmware.device.startswith("nano"): + with pytest.raises(ExceptionRAPDU) as e, APDUS["confirm"].exchange_async(backend): + navigator.navigate_until_text_and_compare( + NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Reject", + default_screenshot_path, + test_name, + ) + assert e.value.status == Errors.SW_DENY + assert len(e.value.data) == 0 + else: + instructions_set = [ + # Test the reject button on both pages + [ + NavInsID.USE_CASE_REVIEW_REJECT, + NavInsID.USE_CASE_STATUS_DISMISS + ], + [ + NavInsID.USE_CASE_REVIEW_TAP, + NavInsID.USE_CASE_ADDRESS_CONFIRMATION_CANCEL, + NavInsID.USE_CASE_STATUS_DISMISS + ] + ] + for i, instructions in enumerate(instructions_set): + with pytest.raises(ExceptionRAPDU) as e, APDUS["confirm"].exchange_async(backend): + navigator.navigate_and_compare( + default_screenshot_path, + test_name + f"/on_page_{i}", + instructions, + ) + assert e.value.status == Errors.SW_DENY + assert len(e.value.data) == 0 diff --git a/tests/test_sign_message.py b/tests/test_sign_message.py new file mode 100644 index 0000000..6f74153 --- /dev/null +++ b/tests/test_sign_message.py @@ -0,0 +1,99 @@ +import pytest +from ragger.error import ExceptionRAPDU +from ragger.navigator import NavInsID + +from raw_apdu_exchange import RawApduExchange +from errors import Errors + +APDUS = { + # Message (ascii): 'Hello world.' + "ascii": RawApduExchange( + "e00a000022048000002c800000f28000000080000000000000000c48656c6c6f20776f726c642e", + "6f59528bdad4c07c54c49c350b219978c8fd27d2b93e6c5c28dee3f1843679a3f71638c55935d239647bcdd794fce8e65e66f8464e7ff5" + "63ce8c5ee84509c00b", + ), + # Message (ascii): 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt + # ut labore et dolore magna aliquyam erat, sed diam voluptua. End.' + "ascii_long": RawApduExchange( + "e00a0000b6048000002c800000f2800000008000000000000000a04c6f72656d20697073756d20646f6c6f722073697420616d65742c20" + "636f6e736574657475722073616469707363696e6720656c6974722c20736564206469616d206e6f6e756d79206569726d6f642074" + "656d706f7220696e766964756e74207574206c61626f726520657420646f6c6f7265206d61676e6120616c69717579616d20657261" + "742c20736564206469616d20766f6c75707475612e20456e642e", + "e8eccbdcf7fa33d031269ec16ba53ce9d28eee91773a864dceca077980bd15e7f9543543cce06bf33525db01a49eddaca4227d17890434" + "97a444f8399a774f0b", + ), + # Message (ascii): 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt + # ut labore et dolore magna aliquyam erat, sed diam voluptua. End...' + "ascii_overlong": RawApduExchange( + "e00a0000b8048000002c800000f2800000008000000000000000a24c6f72656d20697073756d20646f6c6f722073697420616d65742c20" + "636f6e736574657475722073616469707363696e6720656c6974722c20736564206469616d206e6f6e756d79206569726d6f642074" + "656d706f7220696e766964756e74207574206c61626f726520657420646f6c6f7265206d61676e6120616c69717579616d20657261" + "742c20736564206469616d20766f6c75707475612e20456e642e2e2e", + "f01eb0776c988edc723a563acc1351789de922866d769f780c68cd4f88e739281ee0d74dc97f268ee065ccea45870104884d44156bf001" + "63796dd9d153f21a03", + ), + # Message (hex): 'cafecafe' + "hex": RawApduExchange( + "e00a00001a048000002c800000f280000000800000000000000004cafecafe", + "e19c8220c0565482aab9fdab7744b7d6ac632592422c7826e31e3682be01434e32d170a34e533ba8bfa64ff0d94875c9d887eb14bc3098" + "d7521aae156ed5c90f", + ), +} + +def test_sign_message_approve(firmware, backend, navigator, default_screenshot_path, test_name): + for name, apdus in APDUS.items(): + screenshot_folder = test_name + f"_{name}" + with apdus.exchange_async(backend): + if firmware.device.startswith("nano"): + # Manually skip the first page which also contains the word "Sign" + navigator.navigate([NavInsID.RIGHT_CLICK]) + navigator.navigate_until_text_and_compare( + NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Sign", + default_screenshot_path, + screenshot_folder, + screen_change_before_first_instruction = False, + ) + else: + navigator.navigate_until_text_and_compare( + NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_REVIEW_CONFIRM, NavInsID.USE_CASE_STATUS_DISMISS], + "Hold to sign", + default_screenshot_path, + screenshot_folder, + ) + apdus.check_async_response(backend) + +def test_sign_message_reject(firmware, backend, navigator, default_screenshot_path, test_name): + # As the reject flow is the same for all different message types, and is handled mostly by the SDK, we test it only + # for one of the messages, the basic ascii message. + apdus = APDUS["ascii"] + if firmware.device.startswith("nano"): + with pytest.raises(ExceptionRAPDU) as e, apdus.exchange_async(backend): + navigator.navigate_until_text_and_compare( + NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Reject", + default_screenshot_path, + test_name, + ) + assert e.value.status == Errors.SW_DENY + assert len(e.value.data) == 0 + else: + # Test the reject button on the three pages of the ascii message flow. + for i in range(3): + instructions = [NavInsID.USE_CASE_REVIEW_TAP] * i + instructions += [ + NavInsID.USE_CASE_REVIEW_REJECT, + NavInsID.USE_CASE_CHOICE_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS, + ] + with pytest.raises(ExceptionRAPDU) as e, apdus.exchange_async(backend): + navigator.navigate_and_compare( + default_screenshot_path, + test_name + f"/on_page_{i}", + instructions, + ) + assert e.value.status == Errors.SW_DENY + assert len(e.value.data) == 0 diff --git a/tests/test_sign_transaction.py b/tests/test_sign_transaction.py new file mode 100644 index 0000000..7e8e55a --- /dev/null +++ b/tests/test_sign_transaction.py @@ -0,0 +1,319 @@ +import pytest +from ragger.error import ExceptionRAPDU +from ragger.navigator import NavInsID + +from raw_apdu_exchange import RawApduExchange +from errors import Errors + +APDUS = { + # Basic transactions + + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', Recipient Type: '0', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + "basic": RawApduExchange( + "e004000055048000002c800000f28000000080000000010000e677d153553b84db141148ec9d7e77bb55983a2900000000000000000000" + "00000000000000000000000000000000009896800000000000000000000004d2050000", + "e5c55becb7c0873a23ad79c2000038475b14d95a9e49619de6b91e158e2593658758acd5f30693c36c8f9a5edd79668aaf07d01256ab31" + "9ab2c4fa65e6da9d09", + ), + # Version: 'legacy', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', Recipient Type: '0', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + "basic_legacy": RawApduExchange( + "e004000054048000002c800000f28000000080000000000000e677d153553b84db141148ec9d7e77bb55983a2900000000000000000000" + "00000000000000000000000000000000009896800000000000000000000004d20100", + "9687e14d6149acbcf4e400d170b43e4537088c3f32f6f0affe2e861a0ca9924026d175f67c7b3e11e63a0c116bff4e68f99560d41601fe" + "412475f0488cb8180d", + ), + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', Recipient Type: '0', + # Amount: '100', Fee: '0.12345', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + "basic_fee": RawApduExchange( + "e004000055048000002c800000f28000000080000000010000e677d153553b84db141148ec9d7e77bb55983a2900000000000000000000" + "00000000000000000000000000000000009896800000000000003039000004d2050000", + "528b7e3f04e07811ba9c9a6fa282217fdd6993c849b581ddbb565b47af29f17ce2ef23bf9a525d7b679bdf5f1a0330fea8d883c48322c7" + "a3a7db7012597a9a0d", + ), + # Version: 'legacy', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', Recipient Type: '0', + # Amount: '100', Fee: '0.12345', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + "basic_fee_legacy": RawApduExchange( + "e004000054048000002c800000f28000000080000000000000e677d153553b84db141148ec9d7e77bb55983a2900000000000000000000" + "00000000000000000000000000000000009896800000000000003039000004d20100", + "a3551d26679bf81fb2105411e1482b2a0ed0c578751fce3d69098c60d17d4ec8e972d3fb1f65b314a801b04ee892a7881d7c2e532134ce" + "9107d5e1166f8d9605", + ), + + # Transactions with simple data + + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', Recipient Type: '0', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + # Transaction Data (Text): Sender Data Text: '', Recipient Data Text: 'Hello world.' + "data_ascii": RawApduExchange( + "e004000061048000002c800000f2800000008000000001000c48656c6c6f20776f726c642ee677d153553b84db141148ec9d7e77bb5598" + "3a290000000000000000000000000000000000000000000000000000009896800000000000000000000004d2050000", + "f173897b7984d1fff0122c3daba02acea9bc9f331dd131b404cea3c498c6d7f8bc89b8852247874e4e3065d3566b2d70e45ea918c7bd05" + "8a645e52e4db8bc70f", + ), + # Version: 'legacy', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', Recipient Type: '0', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + # Transaction Data (Text): Sender Data Text: '', Recipient Data Text: 'Hello world.' + "data_ascii_legacy": RawApduExchange( + "e004000060048000002c800000f2800000008000000000000c48656c6c6f20776f726c642ee677d153553b84db141148ec9d7e77bb5598" + "3a290000000000000000000000000000000000000000000000000000009896800000000000000000000004d20100", + "fc61daa6869fdd3dd99e8c02f4010082409993f424efed841ceb60644353abb78736f3a76b7a3f1a6864dfb2a5fc6f7627e7f2e0ca853c" + "f99baf3ec119969c0e", + ), + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', Recipient Type: '0', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + # Transaction Data (Hex): Sender Data Hex: '', Recipient Data Hex: 'cafecafe' + "data_binary": RawApduExchange( + "e004000059048000002c800000f28000000080000000010004cafecafee677d153553b84db141148ec9d7e77bb55983a29000000000000" + "0000000000000000000000000000000000000000009896800000000000000000000004d2050000", + "6c30daa99f36d0b17bc77314bac352ea0b6ed4fd06983729686c5058841a7b719542593a37a3b7d5b82e7ddcb71852cb2eec40a76d8ec2" + "3896d64fba3012db0f", + ), + # Version: 'legacy', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', Recipient Type: '0', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + # Transaction Data (Hex): Sender Data Hex: '', Recipient Data Hex: 'cafecafe' + "data_binary_legacy": RawApduExchange( + "e004000058048000002c800000f28000000080000000000004cafecafee677d153553b84db141148ec9d7e77bb55983a29000000000000" + "0000000000000000000000000000000000000000009896800000000000000000000004d20100", + "355e819e876b93e8b2b25fee1c88ee69bb971d4801ce582bd19220934df7a5de2dabb8363fd9ea0c00e280ad01a8bfbb12006f374e3c02" + "2d78484c2739e7e10a", + ), + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', Recipient Type: '0', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + # Transaction Data (Hex): Sender Data Hex: '', Recipient Data Hex: '0082809287' (Cashlink magic number) + "data_cashlink": RawApduExchange( + "e00400005a048000002c800000f280000000800000000100050082809287e677d153553b84db141148ec9d7e77bb55983a290000000000" + "000000000000000000000000000000000000000000009896800000000000000000000004d2050000", + "24b0ad96ec7abf6485c4d91b3deda03a98b2c8a178e5e0dccc19c096ecc15a99a0d15df55095ebd1436bbf1292f0c19633b1be2fa23129" + "2dc514dccf1000d40f", + ), + # Version: 'legacy', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', Recipient Type: '0', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + # Transaction Data (Hex): Sender Data Hex: '', Recipient Data Hex: '0082809287' (Cashlink magic number) + "data_cashlink_legacy": RawApduExchange( + "e004000059048000002c800000f280000000800000000000050082809287e677d153553b84db141148ec9d7e77bb55983a290000000000" + "000000000000000000000000000000000000000000009896800000000000000000000004d20100", + "52327394e8e41d033ebd6a278a1cdb579d9672cfd1c316b4f19a907fd56f7fe83abb68b5d06ddec61f3a5a254d62ff992d7888bb01a153" + "d37e5a174d814ca607", + ), + + # Staking Transactions + # Note that these do not exist for legacy transactions. + + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ77 0000 0000 0000 0000 0000 0000 0000 0001', Recipient Type: '3', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + # Transaction Data (Create Staker): Delegation: '', Signature Proof: '' + "staking_create_staker": RawApduExchange( + "e0040000b9048000002c800000f28000000080000000010064050000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000e677d153553b84db141148ec9d7e77bb55983a2900000000000000000000000000000000" + "00000000010300000000009896800000000000000000000004d2050000", + "27dccfd7c0fe6c820944ec5cd23969ef1b6d0294c1b0c06531f949178b215177e027be855562665ff4d4cac381176770423b65728092c4" + "c509ab24f7c214f70372ca20775e73e6cb8cc32218347949264630f198ddc6ef31d574c8cef68d43ed9743d00005297a52758190cc" + "fcdce22cb7d2a6d4dc34b152e685fdb50ba1f505", + ), + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ77 0000 0000 0000 0000 0000 0000 0000 0001', Recipient Type: '3', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + # Transaction Data (Create Staker): Delegation: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', Signature Proof: '' + "staking_create_staker_delegation": RawApduExchange( + "e0040000cd048000002c800000f28000000080000000010078050100000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000e677d153553b84db141148ec9d7e77bb" + "55983a290000000000000000000000000000000000000000010300000000009896800000000000000000000004d2050000", + "27240bd71d0a553990eca0cb5b919f183d86371b8d717d2189a5b88ab7203bc24b916968adc936912012d0d231c29a3002b212936c6f76" + "bad2114d0b91c1d80000cb4fc956631d2e169dc8f1dd90431f1d09ea708964fcf027cde04e6db09656a6bb70c8704f68ddfd43f459" + "718593eb64de4011853c04aa1bd89fc57e4d150d", + ), + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ77 0000 0000 0000 0000 0000 0000 0000 0001', Recipient Type: '3', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + # Transaction Data (Add Stake): Staker: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9' + "staking_add_stake_sender_staker": RawApduExchange( + "e00400006a048000002c800000f2800000008000000001001506e677d153553b84db141148ec9d7e77bb55983a29e677d153553b84db14" + "1148ec9d7e77bb55983a290000000000000000000000000000000000000000010300000000009896800000000000000000000004d2" + "050000", + "2a38ab898921853eea9855d40e3af98c2839093acf52d91172a5a67d4cb351ff74c3c877ac1cb885295580031b90e56bce942fb5e1ed84" + "2003db45863a08890f", + ), + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ77 0000 0000 0000 0000 0000 0000 0000 0001', Recipient Type: '3', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + # Transaction Data (Add Stake): Staker: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000' + "staking_add_stake_different_staker": RawApduExchange( + "e00400006a048000002c800000f28000000080000000010015060000000000000000000000000000000000000000e677d153553b84db14" + "1148ec9d7e77bb55983a290000000000000000000000000000000000000000010300000000009896800000000000000000000004d2" + "050000", + "5b209d11c1bb84a615ece37731076d46ea6d5175e02d076cef1dd691d12cb77cee45156463e7e3fbf22ed379450b002e9767a58f0fe12a" + "7a6794ad27f2467702", + ), + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ77 0000 0000 0000 0000 0000 0000 0000 0001', Recipient Type: '3', + # Amount: '0', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '2' + # Transaction Data (Update Staker): New Delegation: '', Reactivate All Stake: 'true', Signature Proof: '' + "staking_update_staker_no_new_delegation_and_with_reactivate": RawApduExchange( + "e0040000ba048000002c800000f28000000080000000010065070001000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000e677d153553b84db141148ec9d7e77bb55983a29000000000000000000000000000000" + "0000000000010300000000000000000000000000000000000004d2050200", + "17f06c9fc8e08b18d35877dc50588c34e4b50dcc70f5a6d4623f91b8a2f262d17b760e748b799c2cbaa9d044630a3d0e95ba99c0c9641a" + "eaab91e7674239d302b49e288ef94ec8b7c17280750ae4d1914aa33efd4ad1addadf6e2c9e64927e3d5c7e785bf23ec7bd352d9d31" + "6d278a91d8c87cf1871489d7f90485a65969880b", + ), + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ77 0000 0000 0000 0000 0000 0000 0000 0001', Recipient Type: '3', + # Amount: '0', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '2' + # Transaction Data (Update Staker): New Delegation: 'NQ07 0000 0000 0000 0000 0000 0000 0000 0000', + # Reactivate All Stake: 'false', Signature Proof: '' + "staking_update_staker_with_new_delegation_and_no_reactivate": RawApduExchange( + "e0040000ce048000002c800000f28000000080000000010079070100000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000e677d153553b84db141148ec9d7e77" + "bb55983a290000000000000000000000000000000000000000010300000000000000000000000000000000000004d2050200", + "0088883312ee7ad7cc7d726f497b43083c91046ed80d3f560ec311f2449f765415151e9166f6417727244a9a559643ce689e57bc2a4579" + "bd384eae0128109e018ce3a252b72e4862b54c3935b7b29f38a0b4cc720ffeddfca80295e04bf56ee4b301da4368dedca74df2d665" + "c1f9ed044226874ef47a85d156dc98ceff5d7200", + ), + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ77 0000 0000 0000 0000 0000 0000 0000 0001', Recipient Type: '3', + # Amount: '0', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '2' + # Transaction Data (Set Active State): Amount: '100', Signature Proof: '' + "staking_set_active_stake": RawApduExchange( + "e0040000c0048000002c800000f2800000008000000001006b080000000000989680000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000e677d153553b84db141148ec9d7e77bb55983a29000000000000000000" + "0000000000000000000000010300000000000000000000000000000000000004d2050200", + "ccf6531465a4ac4cdcf0df7b25312741b82d8acb7a612e49d16ae4fa80f842ec958fa6793c7c3cf725ca8b58af612eebcc7ee2067e3316" + "942e9905de7d41ce07d1641885a21321cf06692374aa4525e82d6f172388a2125ecdff2f2c1df30c2f1299fb0ce82e9bea7d81b2e3" + "5f27b172dbf73136a463736ab99a495b9bc8270d", + ), + # Version: 'albatross', + # Sender: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Sender Type: '0', + # Recipient: 'NQ77 0000 0000 0000 0000 0000 0000 0000 0001', Recipient Type: '3', + # Amount: '0', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '2' + # Transaction Data (Retire State): Amount: '100', Signature Proof: '' + "staking_retire_stake": RawApduExchange( + "e0040000c0048000002c800000f2800000008000000001006b090000000000989680000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000e677d153553b84db141148ec9d7e77bb55983a29000000000000000000" + "0000000000000000000000010300000000000000000000000000000000000004d2050200", + "bac243fb66b0769b119596885673d97551532ede3a905601e7225e306a675c31bd06e5c9af8d1f1d4c203853de9e00e57aa3ccb8b65344" + "1cc24d7bf33622f007be9d4a8fc608bf83cee0bec70110cb7c15c92124c2554c49bafd9fa319ac3d7db4ee1fca6aaed0b12b39ba77" + "538160361407d27f51f06383ae8b5ebc77a4b303", + ), + # Version: 'albatross', + # Sender: 'NQ77 0000 0000 0000 0000 0000 0000 0000 0001', Sender Type: '3', + # Recipient: 'NQ13 URTV 2LSM 7E2D N50H 93N9 SYKP PDAR GEH9', Recipient Type: '0', + # Amount: '100', Fee: '0', + # Validity Start Height: '1234', Network: 'test', Flags: '0' + # Transaction Data (Remove Stake): No inputs + "staking_remove_stake": RawApduExchange( + "e004000056048000002c800000f28000000080000000010000000000000000000000000000000000000000000103e677d153553b84db14" + "1148ec9d7e77bb55983a290000000000009896800000000000000000000004d205000101", + "8a2b1cb0557d274366d05735b627d9a273a7d742d0607f6da31d01e53353c5474759aad5d65bbb925a775903ef15eea058c7f2d8cbcc52" + "c34df2962ff9b8440b", + ), +} + +def test_sign_transaction_approve(firmware, backend, navigator, default_screenshot_path, test_name): + for name, apdus in APDUS.items(): + name = name.removesuffix("_legacy") # UI / screenshots are the same for legacy transactions + screenshot_folder = test_name + f"_{name}" + with apdus.exchange_async(backend): + if firmware.device.startswith("nano"): + navigator.navigate_until_text_and_compare( + NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Accept", + default_screenshot_path, + screenshot_folder, + ) + else: + navigator.navigate_until_text_and_compare( + NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_REVIEW_CONFIRM, NavInsID.USE_CASE_STATUS_DISMISS], + "Hold to sign", + default_screenshot_path, + screenshot_folder, + ) + apdus.check_async_response(backend) + +def test_sign_transaction_reject(firmware, backend, navigator, default_screenshot_path, test_name): + # As the reject flow is the same for all different transaction types, and is handled mostly by the SDK, we test it + # only for one of the transactions, the basic transaction. + apdus = APDUS["basic"] + if firmware.device.startswith("nano"): + with pytest.raises(ExceptionRAPDU) as e, apdus.exchange_async(backend): + navigator.navigate_until_text_and_compare( + NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Reject", + default_screenshot_path, + test_name, + ) + assert e.value.status == Errors.SW_DENY + assert len(e.value.data) == 0 + else: + # Test the reject button on the three pages of the basic tx flow. + for i in range(3): + instructions = [NavInsID.USE_CASE_REVIEW_TAP] * i + instructions += [ + NavInsID.USE_CASE_REVIEW_REJECT, + NavInsID.USE_CASE_CHOICE_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS, + ] + with pytest.raises(ExceptionRAPDU) as e, apdus.exchange_async(backend): + navigator.navigate_and_compare( + default_screenshot_path, + test_name + f"/on_page_{i}", + instructions, + ) + assert e.value.status == Errors.SW_DENY + assert len(e.value.data) == 0 diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..e3eea1c --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,8 @@ +from typing import Tuple + +def pop_sized_buf_from_buffer(buffer:bytes, size:int) -> Tuple[bytes, bytes]: + return buffer[size:], buffer[0:size] + +def pop_size_prefixed_buf_from_buf(buffer:bytes) -> Tuple[bytes, int, bytes]: + data_len = buffer[0] + return buffer[1+data_len:], data_len, buffer[1:data_len+1] diff --git a/unit-tests/README.md b/unit-tests/README.md new file mode 100644 index 0000000..37339ce --- /dev/null +++ b/unit-tests/README.md @@ -0,0 +1,5 @@ +# Unit Tests + +The `./unit-tests` directory contains files for testing the transaction parser and the printing utilities. To build and +execute the tests run `./test.sh`. They are currently outdated though and won't compile. The tests will be updated +eventually, some time in the future. diff --git a/test/basicTx.hex b/unit-tests/basicTx.hex similarity index 100% rename from test/basicTx.hex rename to unit-tests/basicTx.hex diff --git a/test/extendedTx.hex b/unit-tests/extendedTx.hex similarity index 100% rename from test/extendedTx.hex rename to unit-tests/extendedTx.hex diff --git a/test/extparsertest.c b/unit-tests/extparsertest.c similarity index 67% rename from test/extparsertest.c rename to unit-tests/extparsertest.c index 14700a6..45b14bb 100644 --- a/test/extparsertest.c +++ b/unit-tests/extparsertest.c @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) { char *filename = argv[1]; uint8_t buffer[130]; - txContent_t txContent; + parsed_tx_t parsed_tx; char expected_expected_data[] = "f! \" # $ % & ' ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~o"; char expected_recipient[] = "NQ12 DHBY 842X 6RP1 Y94E K854 EL9P 77H6 0JJ1"; @@ -39,41 +39,41 @@ int main(int argc, char *argv[]) { int read = read_file(filename, buffer, 4096); if (read) { // printHexBlocks(buffer, read/2); - parseTx(buffer, &txContent); + parse_tx(buffer, &parsed_tx); - if (strcmp(txContent.details1, expected_expected_data) != 0) { + if (strcmp(parsed_tx.details1, expected_expected_data) != 0) { printf( - "parseTx failed on extra data. Expected: %s; Actual: %s\n", + "parse_tx failed on extra data. Expected: %s; Actual: %s\n", expected_expected_data, - txContent.details1 + parsed_tx.details1 ); } - if (strcmp(txContent.recipient, expected_recipient) != 0) { + if (strcmp(parsed_tx.recipient, expected_recipient) != 0) { printf( - "parseTx failed on recipient. Expected: %s; Actual: %s\n", + "parse_tx failed on recipient. Expected: %s; Actual: %s\n", expected_recipient, - txContent.recipient + parsed_tx.recipient ); } - if (strcmp(txContent.value, expected_amount) != 0) { + if (strcmp(parsed_tx.value, expected_amount) != 0) { printf( - "parseTx failed on amount. Expected: %s; Actual: %s\n", + "parse_tx failed on amount. Expected: %s; Actual: %s\n", expected_amount, - txContent.value + parsed_tx.value ); } - if (strcmp(txContent.fee, expected_fee) != 0) { + if (strcmp(parsed_tx.fee, expected_fee) != 0) { printf( - "parseTx failed on fee. Expected: %s; Actual: %s\n", + "parse_tx failed on fee. Expected: %s; Actual: %s\n", expected_fee, - txContent.fee + parsed_tx.fee ); } - if (strcmp(txContent.validity_start, expected_validity_start) != 0) { + if (strcmp(parsed_tx.validity_start, expected_validity_start) != 0) { printf( - "parseTx failed on validity start. Expected: %s; Actual: %s\n", + "parse_tx failed on validity start. Expected: %s; Actual: %s\n", expected_validity_start, - txContent.validity_start + parsed_tx.validity_start ); } } diff --git a/test/parsertest.c b/unit-tests/parsertest.c similarity index 69% rename from test/parsertest.c rename to unit-tests/parsertest.c index e1d7ad4..c692673 100644 --- a/test/parsertest.c +++ b/unit-tests/parsertest.c @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) { char *filename = argv[1]; uint8_t buffer[66]; - txContent_t txContent; + parsed_tx_t parsed_tx; char expected_recipient[] = "NQ15 GQKC ADU7 6KG5 SUEB 80SE P5TL 344P CKKE"; char expected_amount[] = "10 NIM"; @@ -38,34 +38,34 @@ int main(int argc, char *argv[]) { int read = read_file(filename, buffer, 4096); if (read) { // printHexBlocks(buffer, read/2); - parseTx(buffer, &txContent); + parse_tx(buffer, &parsed_tx); - if (strcmp(txContent.recipient, expected_recipient) != 0) { + if (strcmp(parsed_tx.recipient, expected_recipient) != 0) { printf( - "parseTx failed on recipient. Expected: %s; Actual: %s\n", + "parse_tx failed on recipient. Expected: %s; Actual: %s\n", expected_recipient, - txContent.recipient + parsed_tx.recipient ); } - if (strcmp(txContent.value, expected_amount) != 0) { + if (strcmp(parsed_tx.value, expected_amount) != 0) { printf( - "parseTx failed on amount. Expected: %s; Actual: %s\n", + "parse_tx failed on amount. Expected: %s; Actual: %s\n", expected_amount, - txContent.value + parsed_tx.value ); } - if (strcmp(txContent.fee, expected_fee) != 0) { + if (strcmp(parsed_tx.fee, expected_fee) != 0) { printf( - "parseTx failed on fee. Expected: %s; Actual: %s\n", + "parse_tx failed on fee. Expected: %s; Actual: %s\n", expected_fee, - txContent.fee + parsed_tx.fee ); } - if (strcmp(txContent.validity_start, expected_validity_start) != 0) { + if (strcmp(parsed_tx.validity_start, expected_validity_start) != 0) { printf( - "parseTx failed on validity start. Expected: %s; Actual: %s\n", + "parse_tx failed on validity start. Expected: %s; Actual: %s\n", expected_validity_start, - txContent.validity_start + parsed_tx.validity_start ); } } diff --git a/test/test_utils.c b/unit-tests/test_utils.c similarity index 100% rename from test/test_utils.c rename to unit-tests/test_utils.c diff --git a/test/test_utils.h b/unit-tests/test_utils.h similarity index 100% rename from test/test_utils.h rename to unit-tests/test_utils.h diff --git a/test/utilstest.c b/unit-tests/utilstest.c similarity index 100% rename from test/utilstest.c rename to unit-tests/utilstest.c