Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add error codes #297

Merged
merged 9 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 123 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,152 @@
# Ledger Bitcoin Application

## Prerequisite
This is the Bitcoin application for Ledger Nano X/SP, Stax and Flex.

Be sure to have your environment correctly set up (see [Getting Started](https://developers.ledger.com/docs/nano-app/introduction/)) and [ledgerblue](https://pypi.org/project/ledgerblue/) installed.
## Quick start guide

If you want to benefit from [vscode](https://code.visualstudio.com/) integration, it's recommended to move the toolchain in `/opt` and set `BOLOS_ENV` environment variable as follows
### With VSCode

You can quickly setup a convenient environment to build and test your application by using [Ledger's VSCode developer tools extension](https://marketplace.visualstudio.com/items?itemName=LedgerHQ.ledger-dev-tools) which leverages the [ledger-app-dev-tools](https://github.com/LedgerHQ/ledger-app-builder/pkgs/container/ledger-app-builder%2Fledger-app-dev-tools) docker image.

It will allow you, whether you are developing on macOS, Windows or Linux to quickly **build** your apps, **test** them on **Speculos** and **load** them on any supported device.

* Install and run [Docker](https://www.docker.com/products/docker-desktop/).
* 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).
* 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-bitcoin-new` with `git clone git@github.com:LedgerHQ/app-bitcoin-new.git`.
* Open the `app-bitcoin-new` folder with VSCode.
* 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 [Speculos](https://github.com/LedgerHQ/speculos) with `Run with Speculos`.
* You can also run functional tests, load the app on a physical device, and more.

:information_source: The terminal tab of VSCode will show you what commands the extension runs behind the scene.

### With a terminal

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.

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
```
BOLOS_ENV=/opt/bolos-devenv
```

and do the same with `BOLOS_SDK` environment variable
You can then enter this development environment by executing the following command from the directory of the application `git` repository:

**Linux (Ubuntu)**

```shell
sudo docker run --rm -ti --user "$(id -u):$(id -g)" --privileged -v "/dev/bus/usb:/dev/bus/usb" -v "$(realpath .):/app" ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
```
BOLOS_SDK=/opt/nanos-secure-sdk

**macOS**

```shell
sudo docker run --rm -ti --user "$(id -u):$(id -g)" --privileged -v "$(pwd -P):/app" ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
```

## Compilation
**Windows (with PowerShell)**

```shell
docker run --rm -ti --privileged -v "$(Get-Location):/app" 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.

## Compilation and load

To easily setup a development environment for compilation and loading on a physical device, you can use the [VSCode integration](#with-vscode) whether you are on Linux, macOS or Windows.

If you prefer using a terminal to perform the steps manually, you can use the guide below.

### Compilation

Setup a compilation environment by following the [shell with docker approach](#with-a-terminal).

From inside the container, use the following command to build the app :

```shell
make DEBUG=1 # compile optionally with PRINTF
make load # load the app on the Nano using ledgerblue
```

## Documentation
You can choose which device to compile and load for by setting the `BOLOS_SDK` environment variable to the following values :

* `BOLOS_SDK=$NANOX_SDK`
* `BOLOS_SDK=$NANOSP_SDK`
* `BOLOS_SDK=$STAX_SDK`

By default this variable is set to build/load for Nano S+.

The app is compiled for testnet by default. In order to compile the app for mainnet, add `COIN=bitcoin` when running the `make` command.

### Loading on a physical device

This step will vary slightly depending on your platform.

:information_source: Your physical device must be connected, unlocked and the screen showing the dashboard (not inside an application).

**Linux (Ubuntu)**

High level documentation on the architecture and interface of the app:
- [bitcoin.md](doc/bitcoin.md): specifications of application commands.
- [wallet.md](doc/wallet.md): supported wallet signing policies.
- [merkle.md](doc/merkle.md): rationale and specifications for the usage of Merkle trees.
First make sure you have the proper udev rules added on your host :

Additional documentation can be generated with [doxygen](https://www.doxygen.nl)
```shell
# Run these commands on your host, from the app's source folder.
sudo cp .vscode/20-ledger.ledgerblue.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
```

Then once you have [opened a terminal](#with-a-terminal) in the `app-builder` image and [built the app](#compilation-and-load) for the device you want, run the following command :

```shell
# Run this command from the app-builder container terminal.
make load # load the app on a Nano S+ by default
```
doxygen .doxygen/Doxyfile

[Setting the BOLOS_SDK environment variable](#compilation-and-load) will allow you to load on whichever supported device you want.

**macOS / Windows (with PowerShell)**

:information_source: It is assumed you have [Python](https://www.python.org/downloads/) installed on your computer.

Run these commands on your host from the app's source folder once you have [built the app](#compilation-and-load) for the device you want :

```shell
# Install Python virtualenv
python3 -m pip install virtualenv
# Create the 'ledger' virtualenv
python3 -m virtualenv ledger
```

the process outputs HTML and LaTeX documentations in `doc/html` and `doc/latex` folders.
Enter the Python virtual environment

## Client libraries
* macOS : `source ledger/bin/activate`
* Windows : `.\ledger\Scripts\Activate.ps1`

A [Python client library](bitcoin_client), a [TypeScript client library](bitcoin_client_js) and a [Rust client library](bitcoin_client_rs) are available in this repository.
```shell
# Install Ledgerblue (tool to load the app)
python3 -m pip install ledgerblue
# Load the app.
python3 -m ledgerblue.runScript --scp --fileName bin/app.apdu --elfFile bin/app.elf
```

## Tests & Continuous Integration
## Documentation

The flow processed in [GitHub Actions](https://github.com/features/actions) is the following:
For many use cases, the code examples provided in the following client libraries might be sufficient to get started:
- [Python client library](bitcoin_client)
- [JavaScript client library](bitcoin_client_js)
- [Rust client library](bitcoin_client_rs)

- Code formatting with [clang-format](http://clang.llvm.org/docs/ClangFormat.html)
- Compilation of the application for Ledger Nano S in [ledger-app-builder](https://github.com/LedgerHQ/ledger-app-builder)
- Unit tests of C functions with [cmocka](https://cmocka.org/) (see [unit-tests/](unit-tests/))
- End-to-end tests with [Speculos](https://github.com/LedgerHQ/speculos) emulator (see [tests/](tests/))
- Code coverage with [gcov](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html)/[lcov](http://ltp.sourceforge.net/coverage/lcov.php) and upload to [codecov.io](https://about.codecov.io)
- Documentation generation with [doxygen](https://www.doxygen.nl)
If you need to go deeper into the rabbit hole 🐇🕳️, refer to the following documents:
- [bitcoin.md](doc/bitcoin.md): Low-level documentation of the Bitcoin app's communication protocol and commands.
- [merkle.md](doc/merkle.md): Advanced details on techniques used in the Bitcoin app's secured and scalable communication protocol.
- [wallet.md](doc/wallet.md): Information on the types of scripts supported by the Ledger Bitcoin app and the security requirements for multi-user or multi-key spending policies.
- [debugging.md](doc/debugging.md): Guidance on how to diagnose and resolve issues.

It outputs 4 artifacts:
## Tests

- `bitcoin-app-debug` within output files of the compilation process in debug mode
- `code-coverage` within HTML details of code coverage
- `documentation` within HTML auto-generated documentation
See the [tests](tests) folder for documentation on the functional and end-to-end test suites of the bitcoin app, and the [unit-tests](unit-tests) folder for the C unit tests.
2 changes: 1 addition & 1 deletion doc/bitcoin.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The protocol documentation for version from 2.0.0 and before 2.1.0 is [here](./v

### APDUs

The messaging format of the app is compatible with the [APDU protocol](https://developers.ledger.com/docs/nano-app/application-structure/#apdu-interpretation-loop). The `P1` field is reserved for future use and must be set to `0` in all messages. The `P2` field is used as a protocol version identifier; the current version is `1`, while version `0` is still supported. No other value must be used.
The messaging format of the app is compatible with the [APDU protocol](https://developers.ledger.com/docs/device-app/explanation/io#apdu-interpretation-loop). The `P1` field is reserved for future use and must be set to `0` in all messages. The `P2` field is used as a protocol version identifier; the current version is `1`, while version `0` is still supported. No other value must be used.

The main commands use `CLA = 0xE1`, unlike the legacy Bitcoin application that used `CLA = 0xE0`.

Expand Down
23 changes: 23 additions & 0 deletions doc/debugging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
This page contains some information for developers on how to debug failures of the commands of the Ledger Bitcoin app.

# Status Words

Failures in the app's commands are always reported by a corresponding Status Word; see [bitcoin.md](bitcoin.md#status-words) for the currently defined Status Words. Client libraries generally return structured versions of the same Status Words.

The Status Word is contained in the last 2 bytes of the APDU Response, interpreted as a big-endian 16-bit constant.

# Error codes

In addition to the Status Word, some errors provide further details as an _error code_, contained in the first two bytes of the _data_ portion of the reply.

Integrations can ignore the error codes and should not rely on them in production (as they are not guaranteed to be consistent across versions of the app); however, they can provide valuable debugging information for developers working on an integration.

You can see the list of the currently defined error codes in [error_codes.h](../src/error_codes.h).

# Running on Speculos with semihosting

When running the bitcoin app on the [speculos](https://github.com/LedgerHQ/speculos) emulator, additional debugging information can be printed on the command line (on the same terminal where speculos is running) by defining the `DEBUG=10` constant when running the `make` command. See the [ledger-app-builder](https://github.com/LedgerHQ/ledger-app-builder?tab=readme-ov-file#compile-your-app-in-the-container) for instructions on how to build the app for speculos.

Binaries produced in this way _will_ crash if sideloading on a real device.

Note: `DEBUG=10` is a feature of the Bitcoin app, and is not used in other Ledger apps.
2 changes: 1 addition & 1 deletion doc/v0/bitcoin.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This page described the _deprecated_ version 0 of the protocol, as implemented i

### APDUs

The messaging format of the app is compatible with the [APDU protocol](https://developers.ledger.com/docs/nano-app/application-structure/#apdu-interpretation-loop). The `P1` and `P2` fields are reserved for future use and must be set to `0` in all messages.
The messaging format of the app is compatible with the [APDU protocol](https://developers.ledger.com/docs/device-app/explanation/io#apdu-interpretation-loop). The `P1` and `P2` fields are reserved for future use and must be set to `0` in all messages.

The main commands use `CLA = 0xE1`, unlike the legacy Bitcoin application that used `CLA = 0xE0`.

Expand Down
9 changes: 9 additions & 0 deletions src/boilerplate/dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ static inline void SEND_RESPONSE(struct dispatcher_context_s *dc,
dc->send_response();
}

static inline void SEND_SW_EC(struct dispatcher_context_s *dc, uint16_t sw, uint16_t error_code) {
uint8_t error_code_buf[2];
error_code_buf[0] = (error_code >> 8) & 0xFF;
error_code_buf[1] = error_code & 0xFF;
dc->add_to_response(&error_code_buf, sizeof(error_code_buf));
dc->finalize_response(sw);
dc->send_response();
}

/**
bigspider marked this conversation as resolved.
Show resolved Hide resolved
* Describes a command that can be processed by the dispatcher.
*/
Expand Down
110 changes: 110 additions & 0 deletions src/error_codes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#pragma once

/**
* REGISTER_WALLET
*/

// The name of the policy is not acceptable
#define EC_REGISTER_WALLET_UNACCEPTABLE_POLICY_NAME 0x0000

// The wallet policy does not respect the requirement of BIP-388, or the sanity rules of miniscript
#define EC_REGISTER_WALLET_POLICY_NOT_SANE 0x0001

// No key in the wallet policy was recognized as internal.
#define EC_REGISTER_WALLET_POLICY_HAS_NO_INTERNAL_KEY 0x0002

/**
* SIGN_PSBT
*/

// The wallet policy is not standard; it must be registered first and the HMAC must be provided.
#define EC_SIGN_PSBT_MISSING_HMAC_FOR_NONDEFAULT_POLICY 0x0000

// For standard wallet policies, the name must be zero-length (empty).
#define EC_SIGN_PSBT_NO_NAME_FOR_DEFAULT_POLICY 0x0001

// No key in the wallet policy was recognized as internal.
#define EC_SIGN_PSBT_WALLET_POLICY_HAS_NO_INTERNAL_KEY 0x0002

// Depending on the transaction type, at least one of the non-witness UTXO or witness UTXO must be
// present in the PSBT. Check in BIP-174 for the specific requirements for the transaction type.
#define EC_SIGN_PSBT_MISSING_NONWITNESSUTXO_AND_WITNESSUTXO 0x0003

// Failed to check the txid recomputed from the non-witness-utxo. Make sure that the
// non-witness-utxo and the PSBT_IN_PREVIOUS_TXID fields are filled correctly.
#define EC_SIGN_PSBT_NONWITNESSUTXO_CHECK_FAILED 0x0004

// The scriptpubkey or the amount in the non-witness-utxo does not match the one in the
// witness-utxo.
#define EC_SIGN_PSBT_NONWITNESSUTXO_AND_WITNESSUTXO_MISMATCH 0x0005

// Per BIP-174, legacy inputs must have the non-witness-utxo, but no witness-utxo.
#define EC_SIGN_PSBT_MISSING_NONWITNESSUTXO_OR_UNEXPECTED_WITNESSUTXO_FOR_LEGACY 0x0006

// Per BIP-174, all segwit (or taproot) inputs must have the witness-utxo field.
#define EC_SIGN_PSBT_MISSING_WITNESSUTXO_FOR_SEGWIT 0x0007

// If an input has SIGHASH_SINGLE, its index must be less than the number of outputs.
#define EC_SIGN_PSBT_UNALLOWED_SIGHASH_SINGLE 0x0008

// The number of change outputs is larger than the maximum that is allowed.
#define EC_SIGN_PSBT_TOO_MANY_CHANGE_OUTPUTS 0x0009

// The witness script in the PSBT is incorrect.
#define EC_SIGN_PSBT_MISMATCHING_WITNESS_SCRIPT 0x000a

// The redeem Script in the PSBT is incorrect.
#define EC_SIGN_PSBT_MISMATCHING_REDEEM_SCRIPT 0x000b

/**
* Swap
*/

// For swap error codes, the first byte is standardized across apps.
// Refer to the documentation of app-exchange.

// Internal application error, forward to the firmware team for analysis.
bigspider marked this conversation as resolved.
Show resolved Hide resolved
#define EC_SWAP_ERROR_INTERNAL 0x0000

// The amount does not match the one validated in Exchange.
#define EC_SWAP_ERROR_WRONG_AMOUNT 0x0100

// The destination address does not match the one validated in Exchange.
#define EC_SWAP_ERROR_WRONG_DESTINATION 0x0200

// The fees are different from what was validated in Exchange.
#define EC_SWAP_ERROR_WRONG_FEES 0x0300

// The method used is invalid in Exchange context.
#define EC_SWAP_ERROR_WRONG_METHOD 0x0400
// Only default wallet policies can be used in swaps.
#define EC_SWAP_ERROR_WRONG_METHOD_NONDEFAULT_POLICY 0x0401
// No external inputs allowed in swap transactions.
#define EC_SWAP_ERROR_WRONG_METHOD_EXTERNAL_INPUTS 0x0402
// Segwit transaction in swap must have the non-witness UTXO in the PSBT.
#define EC_SWAP_ERROR_WRONG_METHOD_MISSING_NONWITNESSUTXO 0x0403
// Standard swap transaction must have exactly 1 external output.
#define EC_SWAP_ERROR_WRONG_METHOD_WRONG_N_OF_OUTPUTS 0x0404
// Invalid or unsupported script for external output.
#define EC_SWAP_ERROR_WRONG_METHOD_WRONG_UNSUPPORTED_OUTPUT 0x0405

// The mode used for the cross-chain hash validation is not supported.
#define EC_SWAP_ERROR_CROSSCHAIN_WRONG_MODE 0x0500

// The method used is invalid in cross-chain Exchange context.
#define EC_SWAP_ERROR_CROSSCHAIN_WRONG_METHOD 0x0600
// The first output must be OP_RETURN <data> for a cross-chain swap.
#define EC_SWAP_ERROR_CROSSCHAIN_WRONG_METHOD_INVALID_FIRST_OUTPUT 0x0601
// OP_RETURN with non-zero value is not supported.
#define EC_SWAP_ERROR_CROSSCHAIN_WRONG_METHOD_NONZERO_AMOUNT 0x0602

// The hash for the cross-chain transaction does not match the validated value.
#define EC_SWAP_ERROR_CROSSCHAIN_WRONG_HASH 0x0700

// A generic or unspecified error not covered by the specific error codes above. Refer to the
// remaining bytes for further details on the error.
#define EC_SWAP_ERROR_GENERIC 0xFF00
// Unknown swap mode.
#define EC_SWAP_ERROR_GENERIC_UNKNOWN_MODE 0xFF01
// handle_swap_sign_transaction.c::copy_transaction_parameters failed.
#define EC_SWAP_ERROR_GENERIC_COPY_TRANSACTION_PARAMETERS_FAILED 0xFF02
3 changes: 2 additions & 1 deletion src/handler/get_wallet_address.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "../commands.h"
#include "../constants.h"
#include "../crypto.h"
#include "../error_codes.h"
#include "../ui/display.h"
#include "../ui/menu.h"

Expand Down Expand Up @@ -162,7 +163,7 @@ void handler_get_wallet_address(dispatcher_context_t *dc, uint8_t protocol_versi
// Swap feature: check that the wallet policy is a default one
if (G_swap_state.called_from_swap && !is_wallet_default) {
PRINTF("Must be a default wallet policy for swap feature\n");
SEND_SW(dc, SW_FAIL_SWAP);
SEND_SW_EC(dc, SW_FAIL_SWAP, EC_SWAP_ERROR_WRONG_METHOD_NONDEFAULT_POLICY);
finalize_exchange_sign_transaction(false);
}

Expand Down
2 changes: 1 addition & 1 deletion src/handler/lib/policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ __attribute__((warn_unused_result)) static int process_multi_a_sortedmulti_a_nod

// bitvector of used keys (only relevant for sorting keys in SORTEDMULTI)
uint8_t used[BITVECTOR_REAL_SIZE(MAX_PUBKEYS_PER_MULTISIG)];
memset(used, 0, sizeof(memset));
memset(used, 0, sizeof(used));

for (int i = 0; i < policy->n; i++) {
uint8_t compressed_pubkey[33];
Expand Down
Loading
Loading