Skip to content

Commit

Permalink
Merge pull request LedgerHQ#90 from blockydevs/develop
Browse files Browse the repository at this point in the history
Compute budget and split stake instruction support
  • Loading branch information
tdejoigny-ledger authored Sep 11, 2024
2 parents 4d88a45 + a567bc7 commit 787b919
Show file tree
Hide file tree
Showing 201 changed files with 633 additions and 161 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
name: C tests
runs-on: ubuntu-latest
container:
image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-legacy:latest
image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest
steps:
- name: Clone
uses: actions/checkout@v3
Expand Down
140 changes: 53 additions & 87 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,118 +12,84 @@ Current Features:
- Blind sign arbitrary transactions (Enabled via settings)

## Prerequisites

### For building the app

* [Install Docker](https://docs.docker.com/get-docker/)
* For Linux hosts, install the Ledger Nano [udev rules](https://github.com/LedgerHQ/udev-rules)
#### Build the [Ledger App Builder](https://developers.ledger.com/docs/nano-app/build/) Docker image
1. Clone the git repository
```
git clone https://github.com/LedgerHQ/ledger-app-builder.git
```
2. Change directories
```
cd ledger-app-builder
```
3. Checkout the target commit
```
git checkout 73c9e07
```
4. Build the image
```
docker build -t ledger-app-builder:73c9e07 .
```
* If permissions errors are encountered, ensure that your user is in the `docker`
group and that the session has been restarted
* Pull Ledger Development Tools image

### For working with the device
#### Install Python3 PIP
Ubuntu Linux:
```
sudo apt install pip3
```
MacOS
```
brew install python3
```sh
$ docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
```
#### Install ledgerblue python module
```
pip3 install ledgerblue
```
#### Locally clone SDK repos corresponding to those included in the Docker image
* Setup environment
```bash
cat >>"${HOME}"/.profile <<EOF
LEDGER_SDK_BASE_PATH= # set to a writable path
: "${LEDGER_SDK_BASE_PATH:?}"
export NANOS_SDK="${LEDGER_SDK_BASE_PATH}"/nanos-secure-sdk
export NANOX_SDK="${LEDGER_SDK_BASE_PATH}"/nanox-secure-sdk
export NANOSP_SDK="${LEDGER_SDK_BASE_PATH}"/nanosplus-secure-sdk
EOF
```
* Update the current session's environment
```bash
. "${HOME}"/.profile # Or close and reopen your terminal
```
* Clone the repositories
```bash
# Nano S SDK
git clone --branch 2.1.0 --depth 1 https://github.com/LedgerHQ/nanos-secure-sdk.git "${NANOS_SDK}"

# Nano X SDK
git clone --branch 2.0.2-2 --depth 1 https://github.com/LedgerHQ/nanox-secure-sdk.git "${NANOX_SDK}"

# Nano S+ SDK
git clone --branch 1.0.3 --depth 1 https://github.com/LedgerHQ/nanosplus-secure-sdk.git "${NANOSP_SDK}"
```

### For running the test suite
* [Rust](https://rustup.rs/)
* Solana [system dependencies](https://github.com/solana-labs/solana/#1-install-rustc-cargo-and-rustfmt)

## Build
It is highly recommended that you read and understand the [Ledger App Builder](https://developers.ledger.com/docs/embedded-app/build-app/#2-build-the-application)
build process before proceeding. A convenience wrapper script (`./docker-make`) has been provided for simplicity

`docker-make` manages the current target SDK for you, automatically setting `BOLOS_SDK` to the
correct path when the Docker container is launched. A `TARGET_SDK` must be specified when building
from clean and clean must be run _before_ switching
#### `TARGET_SDK`
|Moniker|Device|
|:-----:|:-----|
|s|Nano S|
|x|Nano X|
|sp|Nano S+|

```bash
./docker-make <TARGET_SDK>
Build the app in the container. The BOLOS_SDK variable is used to specify the target SDK, allowing to compile the application for each Ledger device. See [Ledger Application Builder](https://github.com/LedgerHQ/ledger-app-builder?tab=readme-ov-file#compile-your-app-in-the-container) for more details.

```sh
# E.g. for Nano S
$ sudo docker run --rm -ti -v "$(realpath .):/app" --user $(id -u $USER):$(id -g $USER) ghcr.io/ledgerhq/ledger-app-builder//ledger-app-dev-tools:latest
bash$ BOLOS_SDK=$NANOS_SDK make
```

### Clean
```bash
./docker-make clean

Within the running development container

```sh
bash$ BOLOS_SDK=$NANOS_SDK make clean
```

## Working with the device
Requires that the `BOLOS_SDK` envvar [be set](https://developers.ledger.com/docs/embedded-app/build-app/#b-build-the-application).
This can be achieved by first [building](#build) for the desired target device.

See [Ledger Application Builder](https://github.com/LedgerHQ/ledger-app-builder?tab=readme-ov-file#compile-your-app-in-the-container) for more details.

### Load

```bash
make load-only
$ sudo docker run --rm -ti -v "$(realpath .):/app" --privileged -v "/dev/bus/usb:/dev/bus/usb" --user $(id -u $USER):$(id -g $USER) ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
bash$ BOLOS_SDK=$NANOS_SDK make load
```

### Delete
```bash
make delete

Within the running development container

```sh
bash$ BOLOS_SDK=$NANOS_SDK make delete
```

## Test

### Unit

Run C tests:
```bash
make -C libsol

```sh
bash$ make -C libsol
```

### Ragger

Make sure that you have already built the application for the specific device.

Run Ragger tests:

```sh
# Install python test suite dependencies
bash$ pip install -r "tests/python/requirements.txt"

# Run test suite for the specific device, e.g. nanos
bash$ pytest tests/python/ --tb=short -v --device nanos -k ""
```

To regenerate golden snapshots, use `--golden_run` option.

### Integration

First enable `blind-signing` in the App settings
```bash
cargo run --manifest-path tests/Cargo.toml

```sh
bash$ cargo run --manifest-path tests/Cargo.toml
```
43 changes: 0 additions & 43 deletions docker-make

This file was deleted.

1 change: 1 addition & 0 deletions fuzzing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ add_library(sol
${LIBSOL_DIR}/transaction_summary.c
${LIBSOL_DIR}/transaction_printers.c
${LIBSOL_DIR}/vote_instruction.c
${LIBSOL_DIR}/compute_budget_instruction.c
)
target_include_directories(sol PUBLIC ${LIBSOL_DIR}/include)

Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion libsol/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ ifeq ($(COVERAGE),1)
CFLAGS += --coverage
endif

debug_CFLAGS = -g -fsanitize=address -fsanitize=undefined
debug_CFLAGS = -g
release_CFLAGS = -O2

libsol_source_files = $(filter-out %_test.c,$(wildcard *.c))
Expand Down
5 changes: 5 additions & 0 deletions libsol/common_byte_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@
0x7c, 0x7c, 0x35, 0xb5, 0xdd, 0xbc, 0x92, 0xbb, 0x81, 0xe4, 0x1f, 0xa8, 0x40, 0x41, 0x05, \
0x44, 0x8d

#define PROGRAM_ID_COMPUTE_BUDGET /* "ComputeBudget111111111111111111111111111111" */ \
0x03, 0x06, 0x46, 0x6f, 0xe5, 0x21, 0x17, 0x32, 0xff, 0xec, 0xad, 0xba, 0x72, 0xc3, 0x9b, \
0xe7, 0xbc, 0x8c, 0xe5, 0xbb, 0xc5, 0xf7, 0x12, 0x6b, 0x2c, 0x43, 0x9b, 0x3a, 0x40, 0x00, \
0x00, 0x00

// Sysvars

#define SYSVAR_RENT /* "SysvarRent111111111111111111111111111111111" */ \
Expand Down
109 changes: 109 additions & 0 deletions libsol/compute_budget_instruction.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "compute_budget_instruction.h"
#include "sol/transaction_summary.h"

const Pubkey compute_budget_program_id = {{PROGRAM_ID_COMPUTE_BUDGET}};

static int parse_compute_budget_instruction_kind(Parser* parser,
enum ComputeBudgetInstructionKind* kind) {
uint8_t maybe_kind;
BAIL_IF(parse_u8(parser, &maybe_kind));
switch (maybe_kind) {
case ComputeBudgetRequestHeapFrame:
case ComputeBudgetChangeUnitLimit:
case ComputeBudgetChangeUnitPrice:
case ComputeBudgetSetLoadedAccountsDataSizeLimit:
*kind = (enum ComputeBudgetInstructionKind) maybe_kind;
return 0;
default:
return 1;
}
}

static int parse_request_heap_frame_instruction(Parser* parser,
ComputeBudgetRequestHeapFrameInfo* info) {
BAIL_IF(parse_u32(parser, &info->bytes));

return 0;
}

static int parse_unit_limit_instruction(Parser* parse, ComputeBudgetChangeUnitLimitInfo* info) {
BAIL_IF(parse_u32(parse, &info->units));

return 0;
}

static int parse_unit_price_instruction(Parser* parse, ComputeBudgetChangeUnitPriceInfo* info) {
BAIL_IF(parse_u64(parse, &info->units));

return 0;
}

static int parse_loaded_accounts_data_size_limit(
Parser* parse,
ComputeBudgetSetLoadedAccountsDataSizeLimitInfo* info) {
BAIL_IF(parse_u32(parse, &info->units));

return 0;
}

static uint32_t calculate_max_fee(const ComputeBudgetFeeInfo* info) {
uint32_t max_fee = FEE_LAMPORTS_PER_SIGNATURE * info->signatures_count;

if (info->change_unit_price != NULL) {
uint32_t max_compute = 0;
if (info->change_unit_limit != NULL) {
max_compute = info->change_unit_limit->units;
} else {
max_compute =
MIN(info->instructions_count * MAX_CU_PER_INSTRUCTION, MAX_CU_PER_TRANSACTION);
}
return max_fee + (info->change_unit_price->units * max_compute);
}
return max_fee;
}

static int print_compute_budget_max_fee(uint32_t max_fee, const PrintConfig* print_config) {
UNUSED(print_config);

SummaryItem* item;

item = transaction_summary_general_item();
summary_item_set_amount(item, "Max fees", max_fee);

return 0;
}

/**
* Display transaction max fees
* RequestHeapFrame and SetLoadedAccountsDataSizeLimit instruction kinds
* are omitted on purpose as they currently do not display any data on the screen
*/
void print_compute_budget(ComputeBudgetFeeInfo* info, const PrintConfig* print_config) {
uint32_t transaction_max_fee = calculate_max_fee(info);
print_compute_budget_max_fee(transaction_max_fee, print_config);
}

int parse_compute_budget_instructions(const Instruction* instruction,
const MessageHeader* header,
ComputeBudgetInfo* info) {
Parser parser = {instruction->data, instruction->data_length};

BAIL_IF(parse_compute_budget_instruction_kind(&parser, &info->kind));

info->signatures_count = header->pubkeys_header.num_required_signatures;

switch (info->kind) {
case ComputeBudgetRequestHeapFrame:
return parse_request_heap_frame_instruction(&parser, &info->request_heap_frame);
case ComputeBudgetChangeUnitLimit:
return parse_unit_limit_instruction(&parser, &info->change_unit_limit);
case ComputeBudgetChangeUnitPrice:
return parse_unit_price_instruction(&parser, &info->change_unit_price);
case ComputeBudgetSetLoadedAccountsDataSizeLimit:
return parse_loaded_accounts_data_size_limit(
&parser,
&info->set_loaded_accounts_data_size_limit);
default:
return 1;
}
}
Loading

0 comments on commit 787b919

Please sign in to comment.