Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
cd2ee45
Update year in copyrights
ImplOfAnImpl Jun 24, 2026
02d4cf1
Regenerate all snapshots
ImplOfAnImpl Jun 24, 2026
fc8d8c5
Messages renaming and cleanup - WIP
ImplOfAnImpl Jun 18, 2026
ef88f34
Remove unused types from tests/application_client/__init__.py
ImplOfAnImpl Jun 22, 2026
098ff0c
Format python code via `black`
ImplOfAnImpl Jun 22, 2026
8b47b22
Fix rust-analyzer warning about P2_MORE/P2_DONE having wrong case (it…
ImplOfAnImpl Jun 22, 2026
18ee93e
Add a bunch of FIXMEs
ImplOfAnImpl Jun 22, 2026
0043ed0
Send 1st signature after the 1st explicit request rather than after l…
ImplOfAnImpl Jun 22, 2026
e61292f
Update tests: 1) tests fix (as last output no longer returns the 1st …
ImplOfAnImpl Jun 23, 2026
507a6aa
Add test for ts with zero outputs; some cleanup
ImplOfAnImpl Jun 23, 2026
aa5b032
Tests improvements: 1) expect specific intermediate responses; 2) som…
ImplOfAnImpl Jun 23, 2026
6ffed54
Fix tests on touch devices (need snapshot update)
ImplOfAnImpl Jun 23, 2026
2359083
Update snapshots
ImplOfAnImpl Jun 24, 2026
b87fdcd
Add some FIXMEs
ImplOfAnImpl Jun 24, 2026
008a40b
Move the tech spec document to docs and rename it
ImplOfAnImpl Jun 24, 2026
949f6da
Move icons and glyphs to the media subdir; minor cleanup
ImplOfAnImpl Jun 24, 2026
7193d60
Move derivation path handling to utils; add a couple of FIXMEs
ImplOfAnImpl Jun 24, 2026
b941a0c
Minor cleanup of tx messages
ImplOfAnImpl Jun 24, 2026
3c00547
Regression fix: call ui_approve_streaming_review inside switch_to_sig…
ImplOfAnImpl Jun 24, 2026
8ab3bfe
Add some FIXMEs
ImplOfAnImpl Jun 24, 2026
b845883
Fix docs; minor cleanup; forbid non-empty data in ping command
ImplOfAnImpl Jun 24, 2026
b709a13
Appease clippy
ImplOfAnImpl Jun 24, 2026
35f5123
Update snapshots
ImplOfAnImpl Jun 24, 2026
1eaef55
Cleanup
ImplOfAnImpl Jun 24, 2026
54f8186
Use bigger MAX_BUFFER_LEN; add a fixme; fix some typos
ImplOfAnImpl Jun 24, 2026
472ed97
Remove the useless subtitle "Cannot be undone" from message signing r…
ImplOfAnImpl Jun 24, 2026
4d09da8
Add tools/get_target_id.py (copied from the latest boilerplate app)
ImplOfAnImpl Jun 25, 2026
b0dfba3
Cleanup (mostly docs)
ImplOfAnImpl Jun 25, 2026
d0d0acb
Update tests.usage.md to refer to the main readme for device installa…
ImplOfAnImpl Jun 25, 2026
f526cc8
The messages crate now re-exports all types from mintlayer_core_primi…
ImplOfAnImpl Jun 25, 2026
b6838be
Minor cleanup
ImplOfAnImpl Jun 26, 2026
227fa7c
Rename: parsing->processing
ImplOfAnImpl Jun 26, 2026
cb20e34
Fix input/output review titles on nano devices
ImplOfAnImpl Jun 26, 2026
b1c40e7
Rename InputCompressed to SigTarget
ImplOfAnImpl Jun 26, 2026
bcf0534
Encapsulate hashing in the Hasher struct; avoid redundant allocations…
ImplOfAnImpl Jun 26, 2026
d058c83
Apply a FIXME
ImplOfAnImpl Jun 26, 2026
0e33296
Remove redundant FIXMEs in test command sender and apply a small one …
ImplOfAnImpl Jun 26, 2026
1575b34
Change snapshot by-step index increase from 10 to 100
ImplOfAnImpl Jun 26, 2026
04c48f2
Update snapshots
ImplOfAnImpl Jun 26, 2026
929b3b6
Fix CI
ImplOfAnImpl Jun 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 5 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 13 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@ version = "0.1.0"
[workspace.dependencies]
bech32 = { version = "0.11", default-features = false }
chrono = { version = "0.4", default-features = false }
derive_more = { version = "2.1.1", default-features = false }
hex = { version = "0.4.3", default-features = false }
image = "0.25.8"
ledger_device_sdk = "1.35.1"
ledger_secure_sdk_sys = "1.16.1"
num_enum = { version = "0.7.5", default-features = false }
derive_more = { version = "2.1", default-features = false }
hex = { version = "0.4", default-features = false }
image = "0.25"
ledger_device_sdk = "1.35"
ledger_secure_sdk_sys = "1.16"
num_enum = { version = "0.7", default-features = false }
num-traits = { version = "0.2", default-features = false }
# Note: the testmacro crate is published by Ledger and its source code comes from the `testmacro`
# dir inside the sdk repo, i.e. https://github.com/LedgerHQ/ledger-device-rust-sdk/tree/cad196841dbd72c037cfa01bec81a4a3ae57a04e/testmacro
# (though the published version is a bit older).
testmacro = "0.1.0"
testmacro = "0.1"

mintlayer-app-core = { path = "crates/app-core" }
mintlayer-messages = { path = "crates/messages" }
Expand Down Expand Up @@ -68,16 +69,16 @@ path = ["44'/19788'", "44'/1'"]
name = "Mintlayer"

[package.metadata.ledger.nanox]
icon = "icons/mintlayer_14x14.gif"
icon = "media/icons/mintlayer_14x14.gif"

[package.metadata.ledger.nanosplus]
icon = "icons/mintlayer_14x14.gif"
icon = "media/icons/mintlayer_14x14.gif"

[package.metadata.ledger.stax]
icon = "icons/mintlayer_32x32.gif"
icon = "media/icons/mintlayer_32x32.gif"

[package.metadata.ledger.flex]
icon = "icons/mintlayer_40x40.gif"
icon = "media/icons/mintlayer_40x40.gif"

[package.metadata.ledger.apex_p]
icon = "icons/mintlayer_32x32.png"
icon = "media/icons/mintlayer_32x32.png"
120 changes: 77 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

![Rule enforcer](https://github.com/mintlayer/mintlayer-ledger-app/actions/workflows/guidelines_enforcer.yml/badge.svg) ![Build and tests](https://github.com/mintlayer/mintlayer-ledger-app/actions/workflows/build_and_functional_tests.yml/badge.svg)

This is the Mintlayer Ledger application for the Ledger Nano X, S+, Stax, Flex and Nano Gen 5 devices.
This is the Mintlayer Ledger application for the Ledger Nano X, Nano S+, Stax, Flex and Nano Gen 5 devices.

:warning: Nano S is not supported
ℹ️ Nano Gen 5 is usually referred to by its codename, Apex P.

⚠️ Nano S is not supported.

## Quick start guide

### With VS Code

You can quickly setup a development environment on any platform (macOS, Linux or Windows) to build and test your application with [Ledger's VS Code extension](https://marketplace.visualstudio.com/items?itemName=LedgerHQ.ledger-dev-tools).
You can quickly set up a development environment on any platform (macOS, Linux or Windows) to build and test your application with [Ledger's VS Code extension](https://marketplace.visualstudio.com/items?itemName=LedgerHQ.ledger-dev-tools).

By using Ledger's own developer tools [Docker image](https://github.com/LedgerHQ/ledger-app-builder/pkgs/container/ledger-app-builder%2Fledger-app-dev-tools), the extension allows you to **build** your apps with the latest SDK, **test** them on **Speculos** and **load** them on any supported device.

Expand All @@ -19,45 +21,45 @@ By using Ledger's own developer tools [Docker image](https://github.com/LedgerHQ
- On Ubuntu Linux, it should be running by default.
- On macOS, install and launch [XQuartz](https://www.xquartz.org/) (make sure to go to XQuartz > Preferences > Security and check "Allow client connections").
- On Windows, install and launch [VcXsrv](https://sourceforge.net/projects/vcxsrv/) (make sure to configure it to disable access control).
- Install [VScode](https://code.visualstudio.com/download) and add [Ledger's extension](https://marketplace.visualstudio.com/items?itemName=LedgerHQ.ledger-dev-tools).
- Open a terminal and clone `app-boilerplate-rust` with `git clone git@github.com:LedgerHQ/app-boilerplate-rust.git`.
- Open the `app-boilerplate-rust` folder with VSCode.
- Install [VS Code](https://code.visualstudio.com/download) and add [Ledger's extension](https://marketplace.visualstudio.com/items?itemName=LedgerHQ.ledger-dev-tools).
- Open a terminal and clone `mintlayer-ledger-app` with `git clone git@github.com:mintlayer/mintlayer-ledger-app.git`.
- Open the `mintlayer-ledger-app` folder with VS Code.
- Use Ledger extension's sidebar menu or open the tasks menu with `ctrl + shift + b` (`command + shift + b` on a Mac) to conveniently execute actions :
- **Build** the app for the device model of your choice with `Build`.
- **Test** your binary on the [Speculos emulator](https://github.com/LedgerHQ/speculos) with `Run with emulator`.
- You can also **run functional tests**, load the app on a physical device, and more.

ℹ️ The terminal tab of VSCode will show you what commands the extension runs behind the scene.
ℹ️ The terminal tab of VS Code will show you what commands the extension runs behind the scene.

## With a terminal

### Prerequisites

If you do not wish to use the [VS Code extension](#with-vs-code), you can follow the following steps to setup a development environment on Linux, Windows or MacOS.
If you do not wish to use the [VS Code extension](#with-vs-code), you can follow the following steps to set up a development environment on Linux, Windows or macOS.

- The [ledger-app-dev-tools](https://github.com/LedgerHQ/ledger-app-builder/pkgs/container/ledger-app-builder%2Fledger-app-dev-tools) Docker image contains all the required tools and libraries to build, test and load an application on a device. You can download it from the ghcr.io docker repository:

```shell
sudo docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
```
```bash
docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
```

- Make sure you have an X11 server running :
- On Ubuntu Linux, it should be running by default.
- On macOS, install and launch [XQuartz](https://www.xquartz.org/) (make sure to go to XQuartz > Preferences > Security and check "Allow client connections").
- On Windows, install and launch [VcXsrv](https://sourceforge.net/projects/vcxsrv/) (make sure to configure it to disable access control).
- You can then enter into this development environment by executing the following command from the directory of the application (`git` repository):
- Linux (Ubuntu):
```shell
sudo docker run --rm -ti --privileged -v "/dev/bus/usb:/dev/bus/usb" -v "$(realpath .):/app" --publish 5001:5001 --publish 9999:9999 -e DISPLAY=$DISPLAY -v '/tmp/.X11-unix:/tmp/.X11-unix' ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
```
```bash
docker run --user "$(id -u)":"$(id -g)" --rm -ti --privileged -v "/dev/bus/usb:/dev/bus/usb" -v "$(realpath .):/app" --publish 5000:5000 --publish 9999:9999 -e DISPLAY=$DISPLAY -v '/tmp/.X11-unix:/tmp/.X11-unix' ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
```
- macOS:
```shell
sudo docker run --rm -ti --privileged -v "$(pwd -P):/app" --publish 5001:5001 --publish 9999:9999 -e DISPLAY='host.docker.internal:0' -v '/tmp/.X11-unix:/tmp/.X11-unix' ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
```
```bash
docker run --user "$(id -u)":"$(id -g)" --rm -ti --privileged -v "$(pwd -P):/app" --publish 5000:5000 --publish 9999:9999 -e DISPLAY='host.docker.internal:0' -v '/tmp/.X11-unix:/tmp/.X11-unix' ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
```
- Windows (with PowerShell):
```shell
docker run --rm -ti --privileged -v "$(Get-Location):/app" -e DISPLAY='host.docker.internal:0' --publish 5001:5001 --publish 9999:9999 ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
```
```bash
docker run --rm -ti --privileged -v "$(Get-Location):/app" -e DISPLAY='host.docker.internal:0' --publish 5000:5000 --publish 9999:9999 ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
```

The application's code will be available from inside the docker container, you can proceed to the following compilation steps to build your app.

Expand All @@ -69,7 +71,7 @@ You can build the Mintlayer app with the following command executed in the root
cargo ledger build nanox
```

This command will build the app for the Nano X, but you can use any supported device (`nanox`, `nanosplus`, `stax`, `flex`)
This command will build the app for the Nano X, but you can use any supported device (`nanox`, `nanosplus`, `stax`, `flex`, `apex_p`).

### Testing

Expand All @@ -79,15 +81,17 @@ This Mintlayer app comes with functional tests implemented with Ledger's [Ragger

- Install the tests requirements

```bash
pip install -r tests/requirements.txt
```
```bash
pip install -r tests/requirements.txt
```

- Run the functional tests :

```shell
pytest tests/ --tb=short -v --device {nanosp | nanox | stax | flex}
```
```bash
pytest tests/ --tb=short -v --device {nanosp | nanox | stax | flex | apex_p}
```

ℹ️ Speculos uses `nanosp` for Nano S+ (whereas `ledger_app.toml` lists it as `nanos+`).

#### Emulator

Expand All @@ -99,51 +103,81 @@ You can also run the app directly on the [Speculos emulator](https://github.com/
speculos --apdu-port 9999 --api-port 5000 --display headless --model nanosp target/nanosplus/release/mintlayer-app
```

:warning: UI is displayed on `localhost:5000`
⚠️ UI is displayed on `localhost:5000`.

#### Stax or Flex
#### Stax, Flex or Nano Gen 5

```bash
speculos --apdu-port 9999 --api-port 5000 --model stax target/stax/release/mintlayer-app
```

You can also specify the seed phrase with -s "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"

:warning: UI is displayed by your X server
⚠️ UI is displayed by your X server.

You can then send APDU using `ledgercomm` (`pip install ledgercomm`):

```
ledgercomm-send file test.apdu
```

ℹ️ You can also specify the seed phrase via `-s`, e.g.: `-s "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"`.

### Loading on device

:warning: Loading the built application on a device shall be performed out of the Docker container, by using [ledgerctl](https://github.com/LedgerHQ/ledgerctl):
On Linux, loading the application on a device can be done inside the docker container via:

```shell
pip3 install ledgerwallet
```bash
cargo ledger build nanox --load
```
(the `--privileged -v "/dev/bus/usb:/dev/bus/usb"` part of the Docker run command line allows this).

It can also be done outside the docker container on all platforms via [ledgerblue](https://pypi.org/project/ledgerblue/):

* Install `ledgerblue`:
```bash
pip install ledgerblue
```

* Build the app inside the docker container as usual.

* Load the app on device, e.g. for Flex:
```bash
python -m ledgerblue.runScript --targetId <id> --fileName target/flex/release/mintlayer-app.apdu --apdu --scp
```

ℹ️ Your device must be connected, unlocked and the screen showing the dashboard (not inside an application).

For instance, for Flex:
#### About the device target ID

ledgerblue needs the device's `targetId`.

If you call ledgerblue manually, note that its `--targetId` defaults to `0x31100002` (Nano S) — wrong for every other device,
and it is **not** auto-detected from the connected device.
The cleanest option is to let ledgerblue read the id straight from the ELF with `--elfFile`, which overrides `--targetId`:

```bash
python -m ledgerblue.runScript --elfFile target/flex/release/mintlayer-app --fileName target/flex/release/mintlayer-app.apdu --apdu --scp
```

If you instead need the raw target ID value (e.g. for a CI script), the [`tools/get_target_id.py`](tools/get_target_id.py)
helper extracts it from the `ledger.target_id` ELF section:
```bash
ledgerctl install -f target/flex/release/app_flex.json
python tools/get_target_id.py target/flex/release/mintlayer-app # -> 0x33300004
```

## Continuous Integration

The following workflows are executed in [GitHub Actions](https://github.com/features/actions) :

- Ledger guidelines enforcer which verifies that an app is compliant with Ledger guidelines. The successful completion of this reusable workflow is a mandatory step for an app to be available on the Ledger application store. More information on the guidelines can be found in the repository [ledger-app-workflow](https://github.com/LedgerHQ/ledger-app-workflows)
- Compilation of the application for all supported devices in the [ledger-app-builder](https://github.com/LedgerHQ/ledger-app-builder) docker image
- End-to-end tests with the [Speculos](https://github.com/LedgerHQ/speculos) emulator and [ragger](https://github.com/LedgerHQ/ragger) (see [tests/](tests/))
- Ledger guidelines enforcer which verifies that an app is compliant with Ledger guidelines. The successful completion of this reusable workflow is a mandatory step for an app to be available on the Ledger application store. More information on the guidelines can be found in the repository [ledger-app-workflow](https://github.com/LedgerHQ/ledger-app-workflows).
- Compilation of the application for all supported devices in the [ledger-app-builder](https://github.com/LedgerHQ/ledger-app-builder) docker image.
- End-to-end tests with the [Speculos](https://github.com/LedgerHQ/speculos) emulator and [ragger](https://github.com/LedgerHQ/ragger) (see [tests/](tests/)).
- Various lint checks :
- Source code lint checks with `cargo fmt`
- Python functional test code lint checks with `pylint` and `mypy`
- Source code lint checks with `cargo fmt`.
- Python functional test code lint checks with `pylint` and `mypy`.

## Additional documentation

For development guidelines related to the app's memory usage see [docs/memory_usage.md](docs/memory_usage.md).
For the additional documentation, see the [docs](docs/) folder.

- [docs/memory_usage.md](docs/memory_usage.md) - guidelines related to the app's memory usage.
- [docs/technical_specification.md](docs/technical_specification.md) - app's technical specification.
22 changes: 14 additions & 8 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ use std::process::Command;

use image::{ImageFormat, ImageReader, Pixel};

// FIXME: all image files currently contain the Rust logo; need to replace them with Mintlayer logo.

fn main() {
println!("cargo:rerun-if-changed=script.ld");
println!("cargo:rerun-if-changed=icons/mintlayer_14x14.gif");
println!("cargo:rerun-if-changed=icons/mask_14x14.gif");
println!("cargo:rerun-if-changed=media/icons/mintlayer_14x14.gif");
println!("cargo:rerun-if-changed=media/icons/mask_14x14.gif");

let path = std::path::PathBuf::from("icons");
let reader = ImageReader::open(path.join("mintlayer_14x14.gif")).unwrap();
let img = reader.decode().unwrap();
let mut gray = img.into_luma8();
let icons_path = std::path::PathBuf::from("media/icons");
let mut gray = ImageReader::open(icons_path.join("mintlayer_14x14.gif"))
.unwrap()
.decode()
.unwrap()
.into_luma8();

// Apply mask
let mask = ImageReader::open(path.join("mask_14x14.gif"))
let mask = ImageReader::open(icons_path.join("mask_14x14.gif"))
.unwrap()
.decode()
.unwrap()
Expand All @@ -30,7 +34,7 @@ fn main() {
gray.put_pixel(x, y, gray_pixel);
}

let glyph_path = std::path::PathBuf::from("glyphs");
let glyph_path = std::path::PathBuf::from("media/glyphs");
gray.save_with_format(glyph_path.join("home_nano_nbgl.png"), ImageFormat::Png)
.unwrap();

Expand All @@ -42,6 +46,8 @@ fn main() {
let git_hash = String::from_utf8(output.stdout).expect("Failed to convert git output to UTF-8");

// Expose the Git hash as an environment variable
// FIXME: this is unused. Either implement a custom command that would return this info
// (e.g. in the form of a full semantic version), or remove this.
println!("cargo:rustc-env=GIT_HASH={}", git_hash.trim());

// Rerun the build script if .git/HEAD changes
Expand Down
17 changes: 9 additions & 8 deletions crates/app-core/src/app_ui/address.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*****************************************************************************
* Mintlayer Ledger App.
* (c) 2023 Ledger SAS.
* (c) 2025 RBB S.r.l.
* (c) 2025-2026 RBB S.r.l.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,20 +16,21 @@
* limitations under the License.
*****************************************************************************/

use crate::{
app_ui::utils::{compress_public_key, load_glyph, to_address},
StatusWord,
};
use mintlayer_messages::mlcp::{CoinType, Destination, PublicKey};

use ledger_device_sdk::{
ecc::ECPublicKey,
nbgl::{NbglAddressReview, NbglGlyph},
};

use mintlayer_messages::{Destination, PublicKey};

use crate::{
app_ui::utils::{compress_public_key, load_glyph, to_address},
mlcp, StatusWord,
};

pub fn ui_display_pk<const T: char>(
public_key: &ECPublicKey<65, T>,
coin_type: CoinType,
coin_type: mlcp::CoinType,
) -> Result<bool, StatusWord> {
let pk = compress_public_key(public_key)?;

Expand Down
2 changes: 1 addition & 1 deletion crates/app-core/src/app_ui/menu.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*****************************************************************************
* Mintlayer Ledger App.
* (c) 2023 Ledger SAS.
* (c) 2025 RBB S.r.l.
* (c) 2025-2026 RBB S.r.l.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Loading
Loading