diff --git a/.cargo/config b/.cargo/config.toml similarity index 82% rename from .cargo/config rename to .cargo/config.toml index f9458287..5b4b3492 100644 --- a/.cargo/config +++ b/.cargo/config.toml @@ -1,5 +1,5 @@ [unstable] -build-std = ["core"] +build-std = ["core", "alloc"] build-std-features = ["compiler-builtins-mem"] [target.nanosplus] @@ -8,7 +8,6 @@ rustflags = [ "-C", "save-temps", # Fixes incremental builds by keeping .bc and .ll files "-C", "embed-bitcode", ] -runner = "./speculos-wrapper -m nanosp " [target.nanos] rustflags = [ @@ -16,7 +15,6 @@ rustflags = [ "-C", "save-temps", # Fixes incremental builds by keeping .bc and .ll files "-C", "embed-bitcode", ] -runner = "./speculos-wrapper -m nanos " [target.nanox] rustflags = [ @@ -24,7 +22,6 @@ rustflags = [ "-C", "save-temps", # Fixes incremental builds by keeping .bc and .ll files "-C", "embed-bitcode", ] -runner = "./speculos-wrapper -m nanox " [alias] tt = "test --features extra_debug" diff --git a/.github/workflows/build_and_functional_tests.yml b/.github/workflows/build_and_functional_tests.yml index bd7c9910..4d119cf8 100644 --- a/.github/workflows/build_and_functional_tests.yml +++ b/.github/workflows/build_and_functional_tests.yml @@ -18,9 +18,9 @@ jobs: upload_app_binaries_artifact: "compiled_app_binaries" builder: ledger-app-builder - functional_tests: - name: Run tests + ragger_tests: + name: Run ragger tests using the reusable workflow needs: build_application - uses: ./.github/workflows/functional_tests.yml + 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/coding_style_checks.yml b/.github/workflows/coding_style_checks.yml new file mode 100644 index 00000000..d43e087a --- /dev/null +++ b/.github/workflows/coding_style_checks.yml @@ -0,0 +1,22 @@ +name: Run coding style check + +# This workflow will run linting checks to ensure a level of code quality among all Ledger applications. +# +# The presence of this workflow is mandatory as a minimal level of linting is required. + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + - develop-sync + pull_request: + +jobs: + check_linting: + name: Check linting using the reusable workflow + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_lint.yml@v1 + with: + source: './rust-app/src' diff --git a/.github/workflows/functional_tests.yml b/.github/workflows/functional_tests.yml deleted file mode 100644 index 2bd88d50..00000000 --- a/.github/workflows/functional_tests.yml +++ /dev/null @@ -1,110 +0,0 @@ ---- -name: Functional tests - -on: - workflow_call: - inputs: - app_repository: - description: 'The GIT repository to test (defaults to `github.repository`)' - required: false - default: ${{ github.repository }} - type: string - app_branch_name: - description: 'The GIT branch to test (defaults to `github.ref`)' - required: false - default: ${{ github.ref }} - type: string - download_app_binaries_artifact: - description: 'The name of the artifact containing the application binary file(s) to be tested. Required' - required: true - type: string - run_for_devices: - description: | - The list of device(s) on which the test will run. - - Defaults to the full list of device(s) supported by the application as configured in the - 'ledger_app.toml' manifest. - If the manifest is missing, defaults to ALL (["nanos", "nanox", "nanosp", "stax"]). - required: false - default: 'None' - type: string - upload_snapshots_on_failure: - description: 'Enable or disable upload of tests snapshots if the job fails (defaults to true).' - required: false - default: true - type: boolean - test_filter: - description: 'Specify an expression which implements a substring match on the test names' - required: false - default: '""' - type: string - -jobs: - call_get_app_metadata: - # This job digests inputs and repository metadata provided by the `ledger_app.toml` manifest - # file, in order to output relevant directories, compatible devices, and other variables needed - # by following jobs. - name: Retrieve application metadata - uses: LedgerHQ/ledger-app-workflows/.github/workflows/_get_app_metadata.yml@v1 - with: - app_repository: ${{ inputs.app_repository }} - app_branch_name: ${{ inputs.app_branch_name }} - compatible_devices: ${{ inputs.run_for_devices }} - - functional_tests: - name: Functional tests - needs: call_get_app_metadata - strategy: - fail-fast: false - matrix: - device: ${{ fromJSON(needs.call_get_app_metadata.outputs.compatible_devices) }} - runs-on: ubuntu-latest - - steps: - - name: Clone - uses: actions/checkout@v4 - with: - repository: ${{ inputs.app_repository }} - ref: ${{ inputs.app_branch_name }} - submodules: recursive - - - name: Download app binaries - uses: actions/download-artifact@v3 - with: - name: ${{ inputs.download_app_binaries_artifact }} - path: ${{ needs.call_get_app_metadata.outputs.build_directory }}/build/ - - - name: Set Node.js 20.x - uses: actions/setup-node@v3 - with: - node-version: 20.x - - - name: Install yarn - uses: borales/actions-yarn@v4 - with: - cmd: help # No-op, do yarn install later - dir: 'ts-tests' - - - name: Install tests dependencies - run: | - sudo apt-get update && sudo apt-get install -y qemu-user-static tesseract-ocr libtesseract-dev - pip install -U pip setuptools - pip install speculos - - - name: Run test - run: | - BIN_DIR_NAME="$(echo ${{ matrix.device }} | sed 's/nanosp/nanosplus/')" - speculos --api-port 5005 ${{ needs.call_get_app_metadata.outputs.build_directory }}/build/${BIN_DIR_NAME}/release/sui --display headless & - sleep 5 - cd ts-tests - echo "Doing yarn install" - retries=0 - max_retries=10 - while [ $retries -lt $max_retries ]; do - yarn install && break || { - echo "yarn install attempt $retries failed. Retrying..." - retries=$((retries+1)) - sleep 1 - } - done - yarn run test diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index cf30734d..00000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: rustfmt - -on: - push: - branches: [ main, develop, develop-sync ] - pull_request: - branches: [ main, develop, develop-sync ] - workflow_dispatch: - inputs: - name: - description: 'Manually triggered' - -env: - CARGO_TERM_COLOR: always - -jobs: - fmt: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly-2023-11-10 - override: true - components: rust-src, rustfmt - - run: rustup component add rustfmt - - name: Cargo fmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --manifest-path rust-app/Cargo.toml --all -- --check diff --git a/.gitignore b/.gitignore index 6db23ec1..f87ba131 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ Cargo.nix result result-* + +__pycache__ +snapshots-tmp diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f6e92465..ac25e4d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,9 +14,9 @@ These dependencies are needed: Additionally for testing these are needed: - - Node - - Yarn + - Python3 - [Speculos] the official Ledger emulator + - Ragger, a library to run tests on Speculos emulator [Speculos]: https://github.com/ledgerHQ/speculos @@ -27,12 +27,13 @@ The easiest way to get all these dependencies is with Nix: ```bash nix-shell -A $DEVICE.rustShell cd rust-app/ -cargo ledger build -l $DEVICE +cargo build --release --target=$TARGET_JSON ```` where `DEVICE` is one of - - `nanos` for Nano S - `nanox` for Nano X - `nanosplus` for Nano S+ + - `flex`, for Flex + - `stax`, for Stax ### Getting a development environment without Nix @@ -54,12 +55,10 @@ in order to regenerate this file and keep it up to date. ## Running automated tests with Speculos -Using Nix, from the root level of this repo, run: +Using Nix, from the root level of this repo, run the following script to run tests for all devices ```bash -nix-shell -A $DEVICE.rustShell -cd rust-app/ -cargo test --target=$TARGET_JSON +./run-ragger-tests.sh ``` ## Deploying development builds to real hardware @@ -70,7 +69,7 @@ Nix will always track the latest changes, freshly rebuilding components as neede That said, it is also possible to use Cargo build. This useful for the quickest "debug loop". -The [cargo-ledger](https://github.com/LedgerHQ/cargo-ledger.git) builds, outputs a `hex` file and a manifest file for `ledgerctl`, and loads it on a device in a single `cargo-ledger ledger -l nanos` command in the rust-app folder within app directory. +The [cargo-ledger](https://github.com/LedgerHQ/cargo-ledger.git) builds, outputs a `hex` file and a manifest file for `ledgerctl`, and loads it on a device in a single `cargo-ledger ledger -l nanosplus` command in the rust-app folder within app directory. (You do not need to install `cargo-ledger` if you are using the nix-provided development shell, as it provides it.) diff --git a/README.md b/README.md index 86394602..167888cd 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,10 @@ Written using [Alamgu](https://github.com/alamgu/). ## Device Compatability This application is compatible with -- Ledger Nano S, running firmware 2.1.0 and above -- Ledger Nano S+, running firmware 1.1.0 +- Ledger Nano S+ - Ledger Nano X +- Ledger Flex +- Ledger Stax Note: Compatibility with Ledger Nano X is only possible to check on [Speculos](https://github.com/ledgerHQ/speculos/) emulator, because the Nano X does not support side-loading apps under development. @@ -74,9 +75,10 @@ Finally, run the following command to load the app on your device: nix --extra-experimental-features nix-command run -f . $DEVICE.loadApp ``` where `DEVICE` is one of - - `nanos`, for Nano S - `nanox`, for Nano X - `nanosplus`, for Nano S+ + - `flex`, for Flex + - `stax`, for Stax The app will be downloaded (if you have our Nix cache enabled) and/or freshly built as needed. @@ -97,9 +99,10 @@ To build one, run: nix-build -A $DEVICE.tarball ``` where `DEVICE` is one of - - `nanos`, for Nano S - `nanox`, for Nano X - `nanosplus`, for Nano S+ + - `flex`, for Flex + - `stax`, for Stax The last line printed out will be the path of the tarball. @@ -141,9 +144,10 @@ To use this tool using Nix, from the root level of this repo, run this command t nix-shell -A $DEVICE.appShell ``` where `DEVICE` is one of - - `nanos`, for Nano S - `nanox`, for Nano X - `nanosplus`, for Nano S+ + - `flex`, for Flex + - `stax`, for Stax Then, one can use `generic-cli` like this: diff --git a/default.nix b/default.nix index 5418c9c7..094b045f 100644 --- a/default.nix +++ b/default.nix @@ -295,6 +295,8 @@ rec { nanos = appForDevice "nanos"; nanosplus = appForDevice "nanosplus"; nanox = appForDevice "nanox"; + flex = appForDevice "flex"; + stax = appForDevice "stax"; cargoFmtCheck = pkgs.stdenv.mkDerivation { pname = "cargo-fmt-${appName}"; diff --git a/dep/alamgu/github.json b/dep/alamgu/github.json index a18f78b0..3c3730b8 100644 --- a/dep/alamgu/github.json +++ b/dep/alamgu/github.json @@ -3,6 +3,6 @@ "repo": "alamgu", "branch": "develop", "private": false, - "rev": "f41eb60cb2b5145b4948c51898062911c342db6b", - "sha256": "16n2b0irpkgjzp5mrl6vxza0bw7p5c6bmgbfmynbzbcyij5arhnh" + "rev": "7cb39c91dfc9af8c9bc499712df68efb49f694f3", + "sha256": "1b6ndhk24dj6vmyg3qvggfx4zyd90lfdda2qq4g4nf26wi85k6zn" } diff --git a/dep/alamgu/thunk.nix b/dep/alamgu/thunk.nix index bbf2dc18..20f2d28c 100644 --- a/dep/alamgu/thunk.nix +++ b/dep/alamgu/thunk.nix @@ -2,7 +2,10 @@ let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }: if !fetchSubmodules && !private then builtins.fetchTarball { url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256; - } else (import {}).fetchFromGitHub { + } else (import (builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/3aad50c30c826430b0270fcf8264c8c41b005403.tar.gz"; + sha256 = "0xwqsf08sywd23x0xvw4c4ghq0l28w2ki22h0bdn766i16z9q2gr"; +}) {}).fetchFromGitHub { inherit owner repo rev sha256 fetchSubmodules private; }; json = builtins.fromJSON (builtins.readFile ./github.json); diff --git a/dep/ledger-nanos-sdk/github.json b/dep/ledger-nanos-sdk/github.json index 9887d360..2fad1c7d 100644 --- a/dep/ledger-nanos-sdk/github.json +++ b/dep/ledger-nanos-sdk/github.json @@ -1,8 +1,8 @@ { - "owner": "alamgu", - "repo": "ledger-nanos-sdk", - "branch": "alamgu-branch", + "owner": "LedgerHQ", + "repo": "ledger-device-rust-sdk", + "branch": "master", "private": false, - "rev": "52edd548d7456a844b3919969b330e1e21b8e2fa", - "sha256": "1dnlav7883wi7y7m4lhjj9gx4nypa99iwa2acnbydq55pdd1mn6y" + "rev": "e9f89626ea509528f29df74ef775c1a989693450", + "sha256": "0n4v9ibwnkp22dmsdsiyz4hfd7jb83ccfjsv25vbmlcir4s5ppg0" } diff --git a/dep/ledger-nanos-sdk/thunk.nix b/dep/ledger-nanos-sdk/thunk.nix index bbf2dc18..20f2d28c 100644 --- a/dep/ledger-nanos-sdk/thunk.nix +++ b/dep/ledger-nanos-sdk/thunk.nix @@ -2,7 +2,10 @@ let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }: if !fetchSubmodules && !private then builtins.fetchTarball { url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256; - } else (import {}).fetchFromGitHub { + } else (import (builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/3aad50c30c826430b0270fcf8264c8c41b005403.tar.gz"; + sha256 = "0xwqsf08sywd23x0xvw4c4ghq0l28w2ki22h0bdn766i16z9q2gr"; +}) {}).fetchFromGitHub { inherit owner repo rev sha256 fetchSubmodules private; }; json = builtins.fromJSON (builtins.readFile ./github.json); diff --git a/dep/ledger-secure-sdk-nanos/github.json b/dep/ledger-secure-sdk-nanos/github.json index 849017f9..b670b731 100644 --- a/dep/ledger-secure-sdk-nanos/github.json +++ b/dep/ledger-secure-sdk-nanos/github.json @@ -1,7 +1,7 @@ { "owner": "LedgerHQ", - "repo": "nanos-secure-sdk", + "repo": "ledger-secure-sdk", "private": false, - "rev": "62dd047774b5e8a6b4e6158b493ee029453b5bae", - "sha256": "0y0czagzcrhjb5sfwzf6z2alk6227r3j3pgfc8aasg46zxisfrrz" + "rev": "36dd2110ad6349ab84da7bf9d33ecba5df9c4a19", + "sha256": "08icfqc82xxhzsy3r3zzbljas9v9fyjw6fcr3cgw7a8h7ybj2f4s" } diff --git a/dep/ledger-secure-sdk-nanosplus/github.json b/dep/ledger-secure-sdk-nanosplus/github.json index 27045145..035c00cf 100644 --- a/dep/ledger-secure-sdk-nanosplus/github.json +++ b/dep/ledger-secure-sdk-nanosplus/github.json @@ -2,6 +2,6 @@ "owner": "LedgerHQ", "repo": "ledger-secure-sdk", "private": false, - "rev": "48bb8d493548f840ddd261bbe60930f3f5966e7b", - "sha256": "1hgxygkp7qm46fpvd7mkiqzzaz82w510mfk7x4hmw3prkbvrlm3b" + "rev": "cfb10841a1044253190ac0c4f41d43414867fdad", + "sha256": "1kag9lchawp7ngl13wh077cbywm16p1qr9j5sdkv6y2f50wscx3g" } diff --git a/dep/ledger-secure-sdk-nanox/github.json b/dep/ledger-secure-sdk-nanox/github.json index 27045145..035c00cf 100644 --- a/dep/ledger-secure-sdk-nanox/github.json +++ b/dep/ledger-secure-sdk-nanox/github.json @@ -2,6 +2,6 @@ "owner": "LedgerHQ", "repo": "ledger-secure-sdk", "private": false, - "rev": "48bb8d493548f840ddd261bbe60930f3f5966e7b", - "sha256": "1hgxygkp7qm46fpvd7mkiqzzaz82w510mfk7x4hmw3prkbvrlm3b" + "rev": "cfb10841a1044253190ac0c4f41d43414867fdad", + "sha256": "1kag9lchawp7ngl13wh077cbywm16p1qr9j5sdkv6y2f50wscx3g" } diff --git a/dep/ledger_secure_sdk_sys-bindings/github.json b/dep/ledger_secure_sdk_sys-bindings/github.json index b583de36..234b87f5 100644 --- a/dep/ledger_secure_sdk_sys-bindings/github.json +++ b/dep/ledger_secure_sdk_sys-bindings/github.json @@ -3,6 +3,6 @@ "repo": "ledger_secure_sdk_sys-bindings", "branch": "main", "private": false, - "rev": "30be5c603e2faa9c7c6a33dac54a205eb0c150af", - "sha256": "12c9k9p8i4nxvxjkn5j5vl41h97606z19m9z39b2hnk51nq9x5j3" + "rev": "8d3eb75abe5f9ab421215d8c5fd69e6ea7d63cee", + "sha256": "0n0a0g6yjh6l1hnk5k58z05r89zpvqbg6qgrn382vagsnglmkav8" } diff --git a/docker/run-build-in-docker.sh b/docker/run-build-in-docker.sh index 9404b716..8fffabb8 100755 --- a/docker/run-build-in-docker.sh +++ b/docker/run-build-in-docker.sh @@ -3,22 +3,12 @@ set -eu OUT_DIR=/app/docker-outputs -RUST_NANOS_SDK=`mktemp -d` -TARGET_DIR=`mktemp -d` - -git clone --depth=1 "$RUST_NANOS_SDK_GIT" "$RUST_NANOS_SDK" -cd "$RUST_NANOS_SDK"; git checkout "$RUST_NANOS_SDK_REV"; cd - - -PATH=$RUST_NANOS_SDK/ledger_device_sdk:$PATH -export OBJCOPY="llvm-objcopy" -export NM="llvm-nm" - -cd rust-app - -# $RUST_NIGHTLY is set in ledger-app-builder -for device in nanos nanosplus nanox +for device in nanosplus nanox flex stax do - cargo +$RUST_NIGHTLY build --target-dir=$TARGET_DIR --release --target=$RUST_NANOS_SDK/ledger_device_sdk/$device.json -Z build-std=core - cp $TARGET_DIR/$device/release/$APP_NAME $OUT_DIR/$device - chown $HOST_UID:$HOST_GID $OUT_DIR/$device/$APP_NAME + cd rust-app + cargo ledger build $device + cd .. + pytest ragger-tests --tb=short -v --device ${device/nanosplus/nanosp}; + chown -R $HOST_UID:$HOST_GID rust-app/target/ ragger-tests/ + cp rust-app/target/$device/release/$APP_NAME $OUT_DIR/$device done diff --git a/ledger_app.toml b/ledger_app.toml index 534a15a6..af244ab9 100644 --- a/ledger_app.toml +++ b/ledger_app.toml @@ -1,4 +1,7 @@ [app] build_directory = "./rust-app/" sdk = "Rust" -devices = ["nanos", "nanox", "nanos+"] \ No newline at end of file +devices = ["nanox", "nanos+", "flex", "stax"] + +[tests] +pytest_directory = "./ragger-tests/" \ No newline at end of file diff --git a/ragger-tests/.DS_Store b/ragger-tests/.DS_Store new file mode 100644 index 00000000..2d935bf6 Binary files /dev/null and b/ragger-tests/.DS_Store differ diff --git a/ragger-tests/application_client/__init__.py b/ragger-tests/application_client/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ragger-tests/application_client/client.py b/ragger-tests/application_client/client.py new file mode 100644 index 00000000..a68fa460 --- /dev/null +++ b/ragger-tests/application_client/client.py @@ -0,0 +1,220 @@ +from enum import IntEnum +from typing import Dict, List, Optional, Tuple +from hashlib import sha256 +from struct import unpack + +from ragger.backend.interface import BackendInterface, RAPDU +from bip_utils import Bip32Utils + + +MAX_APDU_LEN: int = 255 + +CLA: int = 0x00 +P1: int = 0x00 +P2: int = 0x00 + +class InsType(IntEnum): + GET_VERSION = 0x00 + GET_APP_NAME = 0x00 + VERIFY_ADDRESS = 0x01 + GET_PUBLIC_KEY = 0x02 + SIGN_TX = 0x03 + +class Errors(IntEnum): + SW_DENY = 0x6985 + SW_WRONG_P1P2 = 0x6A86 + SW_INS_NOT_SUPPORTED = 0x6D00 + SW_CLA_NOT_SUPPORTED = 0x6E00 + SW_WRONG_APDU_LENGTH = 0x6E03 + 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 + + +def split_message(message: bytes, max_size: int) -> List[bytes]: + return [message[x:x + max_size] for x in range(0, len(message), max_size)] + + +class Client: + def __init__(self, backend: BackendInterface, use_block_protocol: bool=False) -> None: + self.backend = backend + if use_block_protocol: + self.send_fn = self.send_with_blocks + else: + self.send_fn = self.send_chunks + + def set_use_block_protocol(self, v): + if v: + self.send_fn = self.send_with_blocks + else: + self.send_fn = self.send_chunks + + def get_app_and_version(self) -> Tuple[Tuple[int, int, int], str]: + response = self.send_fn(cla=CLA, + ins=InsType.GET_VERSION, + p1=P1, + p2=P2, + payload=[b""]) + print(response) + major, minor, patch = unpack("BBB", response[:3]) + return ((major, minor, patch), response[3:].decode("ascii")) + + def get_public_key(self, path: str) -> Tuple[int, bytes, int, bytes]: + return self.get_public_key_impl(InsType.GET_PUBLIC_KEY, path) + + def get_public_key_with_confirmation(self, path: str) -> Tuple[int, bytes, int, bytes]: + return self.get_public_key_impl(InsType.VERIFY_ADDRESS, path) + + + def get_public_key_impl(self, ins, path: str) -> Tuple[int, bytes, int, bytes]: + response = self.send_fn(cla=CLA, + ins=ins, + p1=P1, + p2=P2, + payload=[pack_derivation_path(path)]) + response, pub_key_len, pub_key = pop_size_prefixed_buf_from_buf(response) + response, chain_code_len, chain_code = pop_size_prefixed_buf_from_buf(response) + return pub_key_len, pub_key, chain_code_len, chain_code + + + def sign_tx(self, path: str, transaction: bytes) -> bytes: + tx_len = (len(transaction)).to_bytes(4, byteorder='little') + payload = [tx_len + transaction, pack_derivation_path(path)] + return self.send_fn(cla=CLA, + ins=InsType.SIGN_TX, + p1=P1, + p2=P2, + payload=payload) + + def get_async_response(self) -> Optional[RAPDU]: + return self.backend.last_async_response + + def send_chunks(self, cla, ins, p1, p2, payload: [bytes]) -> bytes: + messages = split_message(b''.join(payload), MAX_APDU_LEN) + if messages == []: + messages = [b''] + + result = b'' + + for msg in messages: + # print(f"send_chunks {msg}") + rapdu = self.backend.exchange(cla=cla, + ins=ins, + p1=p1, + p2=p2, + data=msg) + # print(f"send_chunks after {msg}") + result = rapdu.data + + return result + + # Block Protocol + def send_with_blocks(self, cla, ins, p1, p2, payload: [bytes], extra_data: Dict[str, bytes] = {}) -> bytes: + chunk_size = 180 + parameter_list = [] + + if not isinstance(payload, list): + payload = [payload] + + data = {} + + if extra_data: + data.update(extra_data) + + for item in payload: + chunk_list = [] + for i in range(0, len(item), chunk_size): + chunk = item[i:i + chunk_size] + chunk_list.append(chunk) + + last_hash = b'\x00' * 32 + + for chunk in reversed(chunk_list): + linked_chunk = last_hash + chunk + last_hash = sha256(linked_chunk).digest() + data[last_hash.hex()] = linked_chunk + + parameter_list.append(last_hash) + + initialPayload = HostToLedger.START.to_bytes(1, byteorder='little') + b''.join(parameter_list) + + return self.handle_block_protocol(cla, ins, p1, p2, initialPayload, data) + + def handle_block_protocol(self, cla, ins, p1, p2, initialPayload: bytes, data: Dict[str, bytes]) -> bytes: + payload = initialPayload + rv_instruction = -1 + result = b'' + + while (rv_instruction != LedgerToHost.RESULT_FINAL): + rapdu = self.backend.exchange(cla=cla, + ins=ins, + p1=p1, + p2=p2, + data=payload) + rv = rapdu.data + rv_instruction = rv[0] + rv_payload = rv[1:] + + if rv_instruction == LedgerToHost.RESULT_ACCUMULATING: + result = result + rv_payload + payload = HostToLedger.RESULT_ACCUMULATING_RESPONSE.to_bytes(1, byteorder='little') + elif rv_instruction == LedgerToHost.RESULT_FINAL: + result = result + rv_payload + elif rv_instruction == LedgerToHost.GET_CHUNK: + chunk_hash = rv_payload.hex() + if chunk_hash in data: + chunk = data[rv_payload.hex()] + payload = HostToLedger.GET_CHUNK_RESPONSE_SUCCESS.to_bytes(1, byteorder='little') + chunk + else: + payload = HostToLedger.GET_CHUNK_RESPONSE_FAILURE.to_bytes(1, byteorder='little') + elif rv_instruction == LedgerToHost.PUT_CHUNK: + data[sha256(rv_payload).hexdigest()] = rv_payload + payload = HostToLedger.PUT_CHUNK_RESPONSE.to_bytes(1, byteorder='little') + else: + raise RuntimeError("Unknown instruction returned from ledger") + + return result + +class LedgerToHost(IntEnum): + RESULT_ACCUMULATING = 0 + RESULT_FINAL = 1 + GET_CHUNK = 2 + PUT_CHUNK = 3 + +class HostToLedger(IntEnum): + START = 0 + GET_CHUNK_RESPONSE_SUCCESS = 1 + GET_CHUNK_RESPONSE_FAILURE = 2 + PUT_CHUNK_RESPONSE = 3 + RESULT_ACCUMULATING_RESPONSE = 4 + +def pack_derivation_path(derivation_path: str) -> bytes: + split = derivation_path.split("/") + + if split[0] != "m": + raise ValueError("Error master expected") + + path_bytes: bytes = (len(split) - 1).to_bytes(1, byteorder='little') + for value in split[1:]: + if value == "": + raise ValueError(f'Error missing value in split list "{split}"') + if value.endswith('\''): + path_bytes += Bip32Utils.HardenIndex(int(value[:-1])).to_bytes(4, byteorder='little') + else: + path_bytes += int(value).to_bytes(4, byteorder='little') + return path_bytes + +# remainder, data_len, data +def pop_sized_buf_from_buffer(buffer:bytes, size:int) -> Tuple[bytes, bytes]: + return buffer[size:], buffer[0:size] + +# remainder, data_len, data +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/ragger-tests/application_client/py.typed b/ragger-tests/application_client/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/ragger-tests/boil-freeze.txt b/ragger-tests/boil-freeze.txt new file mode 100644 index 00000000..a74fc923 --- /dev/null +++ b/ragger-tests/boil-freeze.txt @@ -0,0 +1,60 @@ +aniso8601==9.0.1 +asn1crypto==1.5.1 +attrs==22.2.0 +bip-utils==2.7.0 +cbor2==5.4.6 +certifi==2022.12.7 +cffi==1.15.1 +charset-normalizer==3.0.1 +click==8.1.3 +coincurve==17.0.0 +construct==2.10.68 +crcmod==1.7 +cryptography==39.0.1 +ecdsa==0.16.1 +ed25519-blake2b==1.4 +exceptiongroup==1.1.0 +Flask==2.1.2 +Flask-RESTful==0.3.9 +hidapi==0.13.1 +idna==3.4 +importlib-metadata==6.0.0 +importlib-resources==5.12.0 +iniconfig==2.0.0 +intelhex==2.3.0 +itsdangerous==2.1.2 +Jinja2==3.1.2 +jsonschema==4.17.3 +ledgerwallet==0.2.3 +MarkupSafe==2.1.2 +mnemonic==0.20 +packaging==23.0 +Pillow==9.4.0 +pkg_resources==0.0.0 +pkgutil_resolve_name==1.3.10 +pluggy==1.0.0 +protobuf==3.20.3 +py-sr25519-bindings==0.1.4 +pycparser==2.21 +pycryptodome==3.17 +pyelftools==0.29 +PyNaCl==1.5.0 +PyQt5==5.15.9 +PyQt5-Qt5==5.15.2 +PyQt5-sip==12.11.1 +pyrsistent==0.19.3 +pysha3==1.0.2 +pytesseract==0.3.10 +pytest==7.2.1 +pytz==2022.7.1 +ragger==1.6.0 +requests==2.28.2 +semver==2.13.0 +six==1.16.0 +speculos==0.1.224 +tabulate==0.9.0 +toml==0.10.2 +tomli==2.0.1 +urllib3==1.26.14 +Werkzeug==2.2.3 +zipp==3.15.0 diff --git a/ragger-tests/conftest.py b/ragger-tests/conftest.py new file mode 100644 index 00000000..04e78abf --- /dev/null +++ b/ragger-tests/conftest.py @@ -0,0 +1,31 @@ +from ragger.conftest import configuration +from ragger.navigator import NavInsID +import pytest +########################### +### 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 + +######################### +### CONFIGURATION END ### +######################### + +# Pull all features from the base ragger conftest using the overridden configuration +pytest_plugins = ("ragger.conftest.base_conftest", ) + +# Notes : +# 1. Remove this fixture once the pending review screen is removed from the app +# 2. This fixture clears the pending review screen before each test +# 3. The scope should be the same as the one configured by BACKEND_SCOPE in +# ragger/conftest/configuration.py +# @pytest.fixture(scope="class", autouse=True) +# def clear_pending_review(firmware, navigator): +# # Press a button to clear the pending review +# if firmware.device.startswith("nano"): +# print("Clearing pending review") +# instructions = [ +# NavInsID.BOTH_CLICK, +# ] +# navigator.navigate(instructions,screen_change_before_first_instruction=False) diff --git a/ragger-tests/requirements.txt b/ragger-tests/requirements.txt new file mode 100644 index 00000000..3de3b0e3 --- /dev/null +++ b/ragger-tests/requirements.txt @@ -0,0 +1,5 @@ +pytest +ragger[speculos,ledgerwallet]>=1.21.1 +ecdsa>=0.18.0,<0.19.1 +safe-pysha3>=1.0.0,<2.0.0 +tomli>=2.0.1 diff --git a/ragger-tests/setup.cfg b/ragger-tests/setup.cfg new file mode 100644 index 00000000..7d0d7e30 --- /dev/null +++ b/ragger-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=100 +extension-pkg-whitelist=hid + +[pycodestyle] +max-line-length = 100 + +[mypy-hid.*] +ignore_missing_imports = True + +[mypy-pytest.*] +ignore_missing_imports = True diff --git a/ragger-tests/snapshots/flex/test_app_mainmenu/00000.png b/ragger-tests/snapshots/flex/test_app_mainmenu/00000.png new file mode 100644 index 00000000..4995808b Binary files /dev/null and b/ragger-tests/snapshots/flex/test_app_mainmenu/00000.png differ diff --git a/ragger-tests/snapshots/flex/test_app_mainmenu/00001.png b/ragger-tests/snapshots/flex/test_app_mainmenu/00001.png new file mode 100644 index 00000000..147e2c1c Binary files /dev/null and b/ragger-tests/snapshots/flex/test_app_mainmenu/00001.png differ diff --git a/ragger-tests/snapshots/flex/test_app_mainmenu/00002.png b/ragger-tests/snapshots/flex/test_app_mainmenu/00002.png new file mode 100644 index 00000000..637b4715 Binary files /dev/null and b/ragger-tests/snapshots/flex/test_app_mainmenu/00002.png differ diff --git a/ragger-tests/snapshots/flex/test_app_mainmenu/00003.png b/ragger-tests/snapshots/flex/test_app_mainmenu/00003.png new file mode 100644 index 00000000..4995808b Binary files /dev/null and b/ragger-tests/snapshots/flex/test_app_mainmenu/00003.png differ diff --git a/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00000.png b/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00000.png new file mode 100644 index 00000000..6a19df8e Binary files /dev/null and b/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00000.png differ diff --git a/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00001.png b/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00001.png new file mode 100644 index 00000000..198dabcb Binary files /dev/null and b/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00001.png differ diff --git a/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00002.png b/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00002.png new file mode 100644 index 00000000..4321e601 Binary files /dev/null and b/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00002.png differ diff --git a/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00003.png b/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00003.png new file mode 100644 index 00000000..4995808b Binary files /dev/null and b/ragger-tests/snapshots/flex/test_get_public_key_confirm_accepted/00003.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_1/00000.png b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_1/00000.png new file mode 100644 index 00000000..acbb161d Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_1/00000.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_1/00001.png b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_1/00001.png new file mode 100644 index 00000000..5d3d552b Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_1/00001.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00000.png b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00000.png new file mode 100644 index 00000000..5d3d552b Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00000.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00001.png b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00001.png new file mode 100644 index 00000000..69cd9efb Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00001.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00002.png b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00002.png new file mode 100644 index 00000000..48357652 Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00002.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00003.png b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00003.png new file mode 100644 index 00000000..be51a9d5 Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00003.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00004.png b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00004.png new file mode 100644 index 00000000..4995808b Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_blind_sign_2/00004.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_refused/00000.png b/ragger-tests/snapshots/flex/test_sign_tx_refused/00000.png new file mode 100644 index 00000000..cb041e67 Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_refused/00000.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_refused/00001.png b/ragger-tests/snapshots/flex/test_sign_tx_refused/00001.png new file mode 100644 index 00000000..c3c6759c Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_refused/00001.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_refused/00002.png b/ragger-tests/snapshots/flex/test_sign_tx_refused/00002.png new file mode 100644 index 00000000..1bf65711 Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_refused/00002.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_refused/00003.png b/ragger-tests/snapshots/flex/test_sign_tx_refused/00003.png new file mode 100644 index 00000000..6fee2ea7 Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_refused/00003.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_refused/00004.png b/ragger-tests/snapshots/flex/test_sign_tx_refused/00004.png new file mode 100644 index 00000000..6a11e118 Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_refused/00004.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_refused/00005.png b/ragger-tests/snapshots/flex/test_sign_tx_refused/00005.png new file mode 100644 index 00000000..4995808b Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_refused/00005.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00000.png b/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00000.png new file mode 100644 index 00000000..cb041e67 Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00000.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00001.png b/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00001.png new file mode 100644 index 00000000..c3c6759c Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00001.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00002.png b/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00002.png new file mode 100644 index 00000000..1bf65711 Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00002.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00003.png b/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00003.png new file mode 100644 index 00000000..6fee2ea7 Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00003.png differ diff --git a/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00004.png b/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00004.png new file mode 100644 index 00000000..4995808b Binary files /dev/null and b/ragger-tests/snapshots/flex/test_sign_tx_sui_transfer/00004.png differ diff --git a/ragger-tests/snapshots/nanosp/test_app_mainmenu/00000.png b/ragger-tests/snapshots/nanosp/test_app_mainmenu/00000.png new file mode 100644 index 00000000..5d8307f3 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_app_mainmenu/00000.png differ diff --git a/ragger-tests/snapshots/nanosp/test_app_mainmenu/00001.png b/ragger-tests/snapshots/nanosp/test_app_mainmenu/00001.png new file mode 100644 index 00000000..500053a6 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_app_mainmenu/00001.png differ diff --git a/ragger-tests/snapshots/nanosp/test_app_mainmenu/00002.png b/ragger-tests/snapshots/nanosp/test_app_mainmenu/00002.png new file mode 100644 index 00000000..265cf5c9 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_app_mainmenu/00002.png differ diff --git a/ragger-tests/snapshots/nanosp/test_app_mainmenu/00003.png b/ragger-tests/snapshots/nanosp/test_app_mainmenu/00003.png new file mode 100644 index 00000000..6196a82b Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_app_mainmenu/00003.png differ diff --git a/ragger-tests/snapshots/nanosp/test_app_mainmenu/00004.png b/ragger-tests/snapshots/nanosp/test_app_mainmenu/00004.png new file mode 100644 index 00000000..5d8307f3 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_app_mainmenu/00004.png differ diff --git a/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00000.png b/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00000.png new file mode 100644 index 00000000..6d71548f Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00000.png differ diff --git a/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00001.png b/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00001.png new file mode 100644 index 00000000..19533e32 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00001.png differ diff --git a/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00002.png b/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00002.png new file mode 100644 index 00000000..f97bd507 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00002.png differ diff --git a/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00003.png b/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00003.png new file mode 100644 index 00000000..2eec8fcc Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00003.png differ diff --git a/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00004.png b/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00004.png new file mode 100644 index 00000000..5d8307f3 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00004.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00000.png b/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00000.png new file mode 100644 index 00000000..18cfed8c Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00000.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00001.png b/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00001.png new file mode 100644 index 00000000..a35b3390 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00001.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00002.png b/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00002.png new file mode 100644 index 00000000..394e4b53 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00002.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00003.png b/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00003.png new file mode 100644 index 00000000..f997230c Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00003.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00004.png b/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00004.png new file mode 100644 index 00000000..b9e0221f Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_blind_sign/00004.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_non_sui_transfer_rejected/00000.png b/ragger-tests/snapshots/nanosp/test_sign_tx_non_sui_transfer_rejected/00000.png new file mode 100644 index 00000000..ebd5d1d0 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_non_sui_transfer_rejected/00000.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_non_sui_transfer_rejected/00001.png b/ragger-tests/snapshots/nanosp/test_sign_tx_non_sui_transfer_rejected/00001.png new file mode 100644 index 00000000..aac3b13c Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_non_sui_transfer_rejected/00001.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_non_sui_transfer_rejected/00002.png b/ragger-tests/snapshots/nanosp/test_sign_tx_non_sui_transfer_rejected/00002.png new file mode 100644 index 00000000..5d8307f3 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_non_sui_transfer_rejected/00002.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00000.png b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00000.png new file mode 100644 index 00000000..31ce1b15 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00000.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00001.png b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00001.png new file mode 100644 index 00000000..289d50ad Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00001.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00002.png b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00002.png new file mode 100644 index 00000000..1289a556 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00002.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00003.png b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00003.png new file mode 100644 index 00000000..c492785a Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00003.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00004.png b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00004.png new file mode 100644 index 00000000..ded4359a Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00004.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00005.png b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00005.png new file mode 100644 index 00000000..fb87aa41 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00005.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00006.png b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00006.png new file mode 100644 index 00000000..dc74eafe Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00006.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00007.png b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00007.png new file mode 100644 index 00000000..2b7e61a4 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00007.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00008.png b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00008.png new file mode 100644 index 00000000..b9e0221f Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00008.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00009.png b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00009.png new file mode 100644 index 00000000..061e1843 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_refused/00009.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00000.png b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00000.png new file mode 100644 index 00000000..31ce1b15 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00000.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00001.png b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00001.png new file mode 100644 index 00000000..289d50ad Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00001.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00002.png b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00002.png new file mode 100644 index 00000000..1289a556 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00002.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00003.png b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00003.png new file mode 100644 index 00000000..c492785a Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00003.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00004.png b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00004.png new file mode 100644 index 00000000..ded4359a Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00004.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00005.png b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00005.png new file mode 100644 index 00000000..fb87aa41 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00005.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00006.png b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00006.png new file mode 100644 index 00000000..dc74eafe Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00006.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00007.png b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00007.png new file mode 100644 index 00000000..2b7e61a4 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00007.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00008.png b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00008.png new file mode 100644 index 00000000..b9e0221f Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_sui_transfer/00008.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_unknown_tx_rejected/00000.png b/ragger-tests/snapshots/nanosp/test_sign_tx_unknown_tx_rejected/00000.png new file mode 100644 index 00000000..ebd5d1d0 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_unknown_tx_rejected/00000.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_unknown_tx_rejected/00001.png b/ragger-tests/snapshots/nanosp/test_sign_tx_unknown_tx_rejected/00001.png new file mode 100644 index 00000000..aac3b13c Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_unknown_tx_rejected/00001.png differ diff --git a/ragger-tests/snapshots/nanosp/test_sign_tx_unknown_tx_rejected/00002.png b/ragger-tests/snapshots/nanosp/test_sign_tx_unknown_tx_rejected/00002.png new file mode 100644 index 00000000..5d8307f3 Binary files /dev/null and b/ragger-tests/snapshots/nanosp/test_sign_tx_unknown_tx_rejected/00002.png differ diff --git a/ragger-tests/snapshots/nanox/test_app_mainmenu/00000.png b/ragger-tests/snapshots/nanox/test_app_mainmenu/00000.png new file mode 100644 index 00000000..5d8307f3 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_app_mainmenu/00000.png differ diff --git a/ragger-tests/snapshots/nanox/test_app_mainmenu/00001.png b/ragger-tests/snapshots/nanox/test_app_mainmenu/00001.png new file mode 100644 index 00000000..500053a6 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_app_mainmenu/00001.png differ diff --git a/ragger-tests/snapshots/nanox/test_app_mainmenu/00002.png b/ragger-tests/snapshots/nanox/test_app_mainmenu/00002.png new file mode 100644 index 00000000..265cf5c9 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_app_mainmenu/00002.png differ diff --git a/ragger-tests/snapshots/nanox/test_app_mainmenu/00003.png b/ragger-tests/snapshots/nanox/test_app_mainmenu/00003.png new file mode 100644 index 00000000..6196a82b Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_app_mainmenu/00003.png differ diff --git a/ragger-tests/snapshots/nanox/test_app_mainmenu/00004.png b/ragger-tests/snapshots/nanox/test_app_mainmenu/00004.png new file mode 100644 index 00000000..5d8307f3 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_app_mainmenu/00004.png differ diff --git a/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00000.png b/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00000.png new file mode 100644 index 00000000..6d71548f Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00000.png differ diff --git a/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00001.png b/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00001.png new file mode 100644 index 00000000..19533e32 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00001.png differ diff --git a/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00002.png b/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00002.png new file mode 100644 index 00000000..f97bd507 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00002.png differ diff --git a/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00003.png b/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00003.png new file mode 100644 index 00000000..2eec8fcc Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00003.png differ diff --git a/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00004.png b/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00004.png new file mode 100644 index 00000000..5d8307f3 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_get_public_key_confirm_accepted/00004.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00000.png b/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00000.png new file mode 100644 index 00000000..18cfed8c Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00000.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00001.png b/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00001.png new file mode 100644 index 00000000..a35b3390 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00001.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00002.png b/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00002.png new file mode 100644 index 00000000..394e4b53 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00002.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00003.png b/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00003.png new file mode 100644 index 00000000..f997230c Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00003.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00004.png b/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00004.png new file mode 100644 index 00000000..b9e0221f Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_blind_sign/00004.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_non_sui_transfer_rejected/00000.png b/ragger-tests/snapshots/nanox/test_sign_tx_non_sui_transfer_rejected/00000.png new file mode 100644 index 00000000..ebd5d1d0 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_non_sui_transfer_rejected/00000.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_non_sui_transfer_rejected/00001.png b/ragger-tests/snapshots/nanox/test_sign_tx_non_sui_transfer_rejected/00001.png new file mode 100644 index 00000000..aac3b13c Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_non_sui_transfer_rejected/00001.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_non_sui_transfer_rejected/00002.png b/ragger-tests/snapshots/nanox/test_sign_tx_non_sui_transfer_rejected/00002.png new file mode 100644 index 00000000..5d8307f3 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_non_sui_transfer_rejected/00002.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_refused/00000.png b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00000.png new file mode 100644 index 00000000..31ce1b15 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00000.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_refused/00001.png b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00001.png new file mode 100644 index 00000000..289d50ad Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00001.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_refused/00002.png b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00002.png new file mode 100644 index 00000000..1289a556 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00002.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_refused/00003.png b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00003.png new file mode 100644 index 00000000..c492785a Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00003.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_refused/00004.png b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00004.png new file mode 100644 index 00000000..ded4359a Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00004.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_refused/00005.png b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00005.png new file mode 100644 index 00000000..fb87aa41 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00005.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_refused/00006.png b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00006.png new file mode 100644 index 00000000..dc74eafe Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00006.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_refused/00007.png b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00007.png new file mode 100644 index 00000000..2b7e61a4 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00007.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_refused/00008.png b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00008.png new file mode 100644 index 00000000..b9e0221f Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00008.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_refused/00009.png b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00009.png new file mode 100644 index 00000000..061e1843 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_refused/00009.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00000.png b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00000.png new file mode 100644 index 00000000..31ce1b15 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00000.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00001.png b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00001.png new file mode 100644 index 00000000..289d50ad Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00001.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00002.png b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00002.png new file mode 100644 index 00000000..1289a556 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00002.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00003.png b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00003.png new file mode 100644 index 00000000..c492785a Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00003.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00004.png b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00004.png new file mode 100644 index 00000000..ded4359a Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00004.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00005.png b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00005.png new file mode 100644 index 00000000..fb87aa41 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00005.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00006.png b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00006.png new file mode 100644 index 00000000..dc74eafe Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00006.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00007.png b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00007.png new file mode 100644 index 00000000..2b7e61a4 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00007.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00008.png b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00008.png new file mode 100644 index 00000000..b9e0221f Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_sui_transfer/00008.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_unknown_tx_rejected/00000.png b/ragger-tests/snapshots/nanox/test_sign_tx_unknown_tx_rejected/00000.png new file mode 100644 index 00000000..ebd5d1d0 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_unknown_tx_rejected/00000.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_unknown_tx_rejected/00001.png b/ragger-tests/snapshots/nanox/test_sign_tx_unknown_tx_rejected/00001.png new file mode 100644 index 00000000..aac3b13c Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_unknown_tx_rejected/00001.png differ diff --git a/ragger-tests/snapshots/nanox/test_sign_tx_unknown_tx_rejected/00002.png b/ragger-tests/snapshots/nanox/test_sign_tx_unknown_tx_rejected/00002.png new file mode 100644 index 00000000..5d8307f3 Binary files /dev/null and b/ragger-tests/snapshots/nanox/test_sign_tx_unknown_tx_rejected/00002.png differ diff --git a/ragger-tests/snapshots/stax/test_app_mainmenu/00000.png b/ragger-tests/snapshots/stax/test_app_mainmenu/00000.png new file mode 100644 index 00000000..d722ab51 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_app_mainmenu/00000.png differ diff --git a/ragger-tests/snapshots/stax/test_app_mainmenu/00001.png b/ragger-tests/snapshots/stax/test_app_mainmenu/00001.png new file mode 100644 index 00000000..8e1f8a1f Binary files /dev/null and b/ragger-tests/snapshots/stax/test_app_mainmenu/00001.png differ diff --git a/ragger-tests/snapshots/stax/test_app_mainmenu/00002.png b/ragger-tests/snapshots/stax/test_app_mainmenu/00002.png new file mode 100644 index 00000000..b5007429 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_app_mainmenu/00002.png differ diff --git a/ragger-tests/snapshots/stax/test_app_mainmenu/00003.png b/ragger-tests/snapshots/stax/test_app_mainmenu/00003.png new file mode 100644 index 00000000..d722ab51 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_app_mainmenu/00003.png differ diff --git a/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00000.png b/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00000.png new file mode 100644 index 00000000..f7905188 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00000.png differ diff --git a/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00001.png b/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00001.png new file mode 100644 index 00000000..6afa9fbe Binary files /dev/null and b/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00001.png differ diff --git a/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00002.png b/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00002.png new file mode 100644 index 00000000..7a494786 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00002.png differ diff --git a/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00003.png b/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00003.png new file mode 100644 index 00000000..d722ab51 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_get_public_key_confirm_accepted/00003.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_1/00000.png b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_1/00000.png new file mode 100644 index 00000000..9696ae12 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_1/00000.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_1/00001.png b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_1/00001.png new file mode 100644 index 00000000..7cf0b9a4 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_1/00001.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00000.png b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00000.png new file mode 100644 index 00000000..7cf0b9a4 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00000.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00001.png b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00001.png new file mode 100644 index 00000000..6215f9ce Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00001.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00002.png b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00002.png new file mode 100644 index 00000000..4cbc9da2 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00002.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00003.png b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00003.png new file mode 100644 index 00000000..392165d4 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00003.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00004.png b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00004.png new file mode 100644 index 00000000..d722ab51 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_blind_sign_2/00004.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_blindsign_disabled/00000.png b/ragger-tests/snapshots/stax/test_sign_tx_blindsign_disabled/00000.png new file mode 100644 index 00000000..b56a7728 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_blindsign_disabled/00000.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_refused/00000.png b/ragger-tests/snapshots/stax/test_sign_tx_refused/00000.png new file mode 100644 index 00000000..43c57401 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_refused/00000.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_refused/00001.png b/ragger-tests/snapshots/stax/test_sign_tx_refused/00001.png new file mode 100644 index 00000000..523ac321 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_refused/00001.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_refused/00002.png b/ragger-tests/snapshots/stax/test_sign_tx_refused/00002.png new file mode 100644 index 00000000..3cf13af0 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_refused/00002.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_refused/00003.png b/ragger-tests/snapshots/stax/test_sign_tx_refused/00003.png new file mode 100644 index 00000000..a374e5ad Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_refused/00003.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_refused/00004.png b/ragger-tests/snapshots/stax/test_sign_tx_refused/00004.png new file mode 100644 index 00000000..abc9677f Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_refused/00004.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_refused/00005.png b/ragger-tests/snapshots/stax/test_sign_tx_refused/00005.png new file mode 100644 index 00000000..d722ab51 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_refused/00005.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00000.png b/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00000.png new file mode 100644 index 00000000..43c57401 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00000.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00001.png b/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00001.png new file mode 100644 index 00000000..523ac321 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00001.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00002.png b/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00002.png new file mode 100644 index 00000000..3cf13af0 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00002.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00003.png b/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00003.png new file mode 100644 index 00000000..a374e5ad Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00003.png differ diff --git a/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00004.png b/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00004.png new file mode 100644 index 00000000..d722ab51 Binary files /dev/null and b/ragger-tests/snapshots/stax/test_sign_tx_sui_transfer/00004.png differ diff --git a/ragger-tests/test_app_mainmenu.py b/ragger-tests/test_app_mainmenu.py new file mode 100644 index 00000000..52cd7084 --- /dev/null +++ b/ragger-tests/test_app_mainmenu.py @@ -0,0 +1,23 @@ +from ragger.navigator import NavInsID + +from utils import ROOT_SCREENSHOT_PATH + + +# In this test we check the behavior of the device main menu +def test_app_mainmenu(firmware, navigator, test_name): + # Navigate in the main menu + if firmware.device.startswith("nano"): + instructions = [ + NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK + ] + else: + instructions = [ + NavInsID.USE_CASE_HOME_SETTINGS, + NavInsID.USE_CASE_SUB_SETTINGS_NEXT, + NavInsID.USE_CASE_SUB_SETTINGS_EXIT + ] + navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, instructions, + screen_change_before_first_instruction=False) diff --git a/ragger-tests/test_pubkey_cmd.py b/ragger-tests/test_pubkey_cmd.py new file mode 100644 index 00000000..b0c6b046 --- /dev/null +++ b/ragger-tests/test_pubkey_cmd.py @@ -0,0 +1,37 @@ +import pytest + +from application_client.client import Client, Errors +from contextlib import contextmanager +from ragger.bip import calculate_public_key_and_chaincode, CurveChoice +from ragger.error import ExceptionRAPDU +from ragger.navigator import NavInsID, NavIns +from utils import ROOT_SCREENSHOT_PATH, run_apdu_and_nav_tasks_concurrently + + +# In this test we check that the GET_PUBLIC_KEY works in non-confirmation mode +def test_get_public_key_no_confirm(backend): + for path in [ "m/44'/784'/0'"]: + client = Client(backend, use_block_protocol=True) + _, public_key, _, address = client.get_public_key(path=path) + + assert public_key.hex() == "6fc6f39448ad7af0953b78b16d0f840e6fe718ba4a89384239ff20ed088da2fa" + assert address.hex() == "56b19e720f3bfa8caaef806afdd5dfaffd0d6ec9476323a14d1638ad734b2ba5" + + +# In this test we check that the GET_PUBLIC_KEY works in confirmation mode +def test_get_public_key_confirm_accepted(backend, scenario_navigator, firmware, navigator): + client = Client(backend, use_block_protocol=True) + path = "m/44'/784'/0'" + + def nav_task(): + scenario_navigator.address_review_approve() + + def apdu_task(): + return client.get_public_key_with_confirmation(path=path) + + def check_result(result): + _, public_key, _, address = result + assert public_key.hex() == "6fc6f39448ad7af0953b78b16d0f840e6fe718ba4a89384239ff20ed088da2fa" + assert address.hex() == "56b19e720f3bfa8caaef806afdd5dfaffd0d6ec9476323a14d1638ad734b2ba5" + + run_apdu_and_nav_tasks_concurrently(apdu_task, nav_task, check_result) diff --git a/ragger-tests/test_sign_cmd.py b/ragger-tests/test_sign_cmd.py new file mode 100644 index 00000000..b89aa579 --- /dev/null +++ b/ragger-tests/test_sign_cmd.py @@ -0,0 +1,244 @@ +import pytest +import concurrent.futures +import time +import base64 + +from application_client.client import Client, Errors +from contextlib import contextmanager +from ragger.error import ExceptionRAPDU +from ragger.navigator import NavIns, NavInsID +from utils import ROOT_SCREENSHOT_PATH, check_signature_validity, run_apdu_and_nav_tasks_concurrently + +# can sign a simple Sui transfer transaction +def test_sign_tx_sui_transfer(backend, scenario_navigator, firmware, navigator): + client = Client(backend, use_block_protocol=True) + path = "m/44'/784'/0'" + + _, public_key, _, _ = client.get_public_key(path=path) + assert len(public_key) == 32 + + transaction = bytes.fromhex('000000000002000840420f000000000000204f2370b2a4810ad6c8e1cfd92cc8c8818fef8f59e3a80cea17871f78d850ba4b0202000101000001010200000101006fb21feead027da4873295affd6c4f3618fe176fa2fbf3e7b5ef1d9463b31e210112a6d0c44edc630d2724b1f57fea4f93308b1d22164402c65778bd99379c4733070000000000000020f2fd3c87b227f1015182fe4348ed680d7ed32bcd3269704252c03e1d0b13d30d6fb21feead027da4873295affd6c4f3618fe176fa2fbf3e7b5ef1d9463b31e2101000000000000000c0400000000000000') + + def apdu_task(): + return client.sign_tx(path=path, transaction=transaction) + + def nav_task(): + if firmware.device.startswith("nano"): + navigator.navigate_and_compare( + instructions=[ NavInsID.RIGHT_CLICK # Transfer SUI + , NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK # From ... + , NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK # To ... + , NavInsID.RIGHT_CLICK # Amount + , NavInsID.RIGHT_CLICK # Max Gas + , NavInsID.RIGHT_CLICK # Sign Transaction? + , NavInsID.BOTH_CLICK + ] + , timeout=10 + , test_case_name="test_sign_tx_sui_transfer" + , path=scenario_navigator.screenshot_path + , screen_change_before_first_instruction=True + , screen_change_after_last_instruction=False + ) + else: + scenario_navigator.review_approve() + + def check_result(result): + assert len(result) == 64 + assert check_signature_validity(public_key, result, transaction) + + run_apdu_and_nav_tasks_concurrently(apdu_task, nav_task, check_result) + +# can blind sign an unknown transaction +def test_sign_tx_blind_sign(backend, scenario_navigator, firmware, navigator): + client = Client(backend, use_block_protocol=True) + path = "m/44'/784'/0'" + + _, public_key, _, _ = client.get_public_key(path=path) + + transaction = bytes.fromhex('00000000050205546e7f126d2f40331a543b9608439b582fd0d103000000000000002080fdabcc90498e7eb8413b140c4334871eeafa5a86203fd9cfdb032f604f49e1284af431cf032b5d85324135bf9a3073e920d7f5020000000000000020a06f410c175e828c24cee84cb3bd95cff25c33fbbdcb62c6596e8e423784ffe702d08074075c7097f361e8b443e2075a852a2292e8a08074075c7097f361e8b443e2075a852a2292e80180969800000000001643fb2578ff7191c643079a62c1cca8ec2752bc05546e7f126d2f40331a543b9608439b582fd0d103000000000000002080fdabcc90498e7eb8413b140c4334871eeafa5a86203fd9cfdb032f604f49e101000000000000002c01000000000000') + + def apdu_task(): + return client.sign_tx(path=path, transaction=transaction) + + def nav_task(): + if firmware.device.startswith("nano"): + navigator.navigate_and_compare( + instructions=[ NavInsID.RIGHT_CLICK # Warning... + , NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK # Transaction Hash + , NavInsID.RIGHT_CLICK # Blind Sign Transaction? + , NavInsID.BOTH_CLICK] + , timeout=10 + , path=scenario_navigator.screenshot_path + , test_case_name="test_sign_tx_blind_sign" + , screen_change_before_first_instruction=False + , screen_change_after_last_instruction=False + ) + else: + # Dismiss the "Blind signing ahead" screen + navigator.navigate_and_compare( + instructions=[NavInsID.USE_CASE_CHOICE_REJECT] + , timeout=20 + , path=scenario_navigator.screenshot_path + , test_case_name="test_sign_tx_blind_sign_1" + , screen_change_before_first_instruction=True + , screen_change_after_last_instruction=True + ) + # Below is similar to scenario_navigator.review_approve() + # But screen_change_before_first_instruction=True causes hang + navigator.navigate_until_text_and_compare( + navigate_instruction=NavInsID.SWIPE_CENTER_TO_LEFT + , validation_instructions=[NavInsID.USE_CASE_REVIEW_CONFIRM, NavInsID.USE_CASE_STATUS_DISMISS] + , text="^Hold to sign$" + , timeout=20 + , path=scenario_navigator.screenshot_path + , test_case_name="test_sign_tx_blind_sign_2" + , screen_change_before_first_instruction=False + , screen_change_after_last_instruction=True + ) + + def check_result(result): + assert len(result) == 64 + assert check_signature_validity(public_key, result, transaction) + + with blind_sign_enabled(firmware, navigator): + run_apdu_and_nav_tasks_concurrently(apdu_task, nav_task, check_result) + +# Transaction signature refused test +# The test will ask for a transaction signature that will be refused on screen +def test_sign_tx_refused(backend, scenario_navigator, firmware, navigator): + client = Client(backend, use_block_protocol=True) + path = "m/44'/784'/0'" + + transaction = bytes.fromhex('000000000002000840420f000000000000204f2370b2a4810ad6c8e1cfd92cc8c8818fef8f59e3a80cea17871f78d850ba4b0202000101000001010200000101006fb21feead027da4873295affd6c4f3618fe176fa2fbf3e7b5ef1d9463b31e210112a6d0c44edc630d2724b1f57fea4f93308b1d22164402c65778bd99379c4733070000000000000020f2fd3c87b227f1015182fe4348ed680d7ed32bcd3269704252c03e1d0b13d30d6fb21feead027da4873295affd6c4f3618fe176fa2fbf3e7b5ef1d9463b31e2101000000000000000c0400000000000000') + + def apdu_task(): + return client.sign_tx(path=path, transaction=transaction) + + def nav_task(): + if firmware.device.startswith("nano"): + navigator.navigate_and_compare( + instructions=[ NavInsID.RIGHT_CLICK # Transfer SUI + , NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK # From ... + , NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK # To ... + , NavInsID.RIGHT_CLICK # Amount + , NavInsID.RIGHT_CLICK # Max Gas + , NavInsID.RIGHT_CLICK # Sign Transaction? + , NavInsID.RIGHT_CLICK # Confirm + , NavInsID.BOTH_CLICK + ] + , timeout=10 + , test_case_name="test_sign_tx_refused" + , path=scenario_navigator.screenshot_path + , screen_change_before_first_instruction=True + , screen_change_after_last_instruction=False + ) + else: + scenario_navigator.review_reject() + + def check_result(result): + pytest.fail('should not happen') + + with pytest.raises(ExceptionRAPDU) as e: + run_apdu_and_nav_tasks_concurrently(apdu_task, nav_task, check_result) + + assert len(e.value.data) == 0 + +# should reject signing a non-SUI coin transaction, if blind signing is not enabled +def test_sign_tx_non_sui_transfer_rejected(backend, scenario_navigator, firmware, navigator): + client = Client(backend, use_block_protocol=True) + path = "m/44'/784'/0'" + + _, public_key, _, _ = client.get_public_key(path=path) + assert len(public_key) == 32 + + transaction = base64.b64decode('AAAAAAADAQAe2uv1Mds+xCVK5Jv/Dv5cgEl/9DthDcpbjWcsmFpzbs6BNQAAAAAAIKPD8GQqgBpJZRV+nFDRE7rqR0Za8x0pyfLusVdpPPVRAAgADl+jHAAAAAAg5y3MHATlk+Ik5cPIdEz5iPANs1jcXZHVGjh4Mb16lwkCAgEAAAEBAQABAQIAAAECAF/sd27xyQe/W+gY4WRtPlQro1siWQu79s0pxbbCSRafAfnjaU5yJSFFDJznsAaBqbkiR9CB8DJqWki8fn8AUZeQz4E1AAAAAAAgTRU/MsawTJirpVwjDF8gyiEbaT0+7J0V8ifUEGGBkcVf7Hdu8ckHv1voGOFkbT5UK6NbIlkLu/bNKcW2wkkWn+gDAAAAAAAA8NdGAAAAAAAA') + + def apdu_task(): + return client.sign_tx(path=path, transaction=transaction) + + def nav_task(): + if firmware.device.startswith("nano"): + navigator.navigate_and_compare( + instructions=[NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK] + , timeout=10 + , test_case_name="test_sign_tx_non_sui_transfer_rejected" + , path=scenario_navigator.screenshot_path + , screen_change_before_first_instruction=True + , screen_change_after_last_instruction=False + ) + else: + # Dismiss the "Enable Blind signing" screen + navigator.navigate([NavInsID.USE_CASE_CHOICE_REJECT], + screen_change_before_first_instruction=False, + screen_change_after_last_instruction=False) + + def check_result(result): + pytest.fail('should not happen') + + with pytest.raises(ExceptionRAPDU) as e: + run_apdu_and_nav_tasks_concurrently(apdu_task, nav_task, check_result) + + assert len(e.value.data) == 0 + +# should reject signing an unknown transaction, if blind signing is not enabled +def test_sign_tx_unknown_tx_rejected(backend, scenario_navigator, firmware, navigator): + client = Client(backend, use_block_protocol=True) + path = "m/44'/784'/0'" + + _, public_key, _, _ = client.get_public_key(path=path) + assert len(public_key) == 32 + + transaction = bytes.fromhex('00000000050205546e7f126d2f40331a543b9608439b582fd0d103000000000000002080fdabcc90498e7eb8413b140c4334871eeafa5a86203fd9cfdb032f604f49e1284af431cf032b5d85324135bf9a3073e920d7f5020000000000000020a06f410c175e828c24cee84cb3bd95cff25c33fbbdcb62c6596e8e423784ffe702d08074075c7097f361e8b443e2075a852a2292e8a08074075c7097f361e8b443e2075a852a2292e80180969800000000001643fb2578ff7191c643079a62c1cca8ec2752bc05546e7f126d2f40331a543b9608439b582fd0d103000000000000002080fdabcc90498e7eb8413b140c4334871eeafa5a86203fd9cfdb032f604f49e101000000000000002c01000000000000') + + def apdu_task(): + return client.sign_tx(path=path, transaction=transaction) + + def nav_task(): + if firmware.device.startswith("nano"): + navigator.navigate_and_compare( + instructions=[NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK] + , timeout=10 + , test_case_name="test_sign_tx_unknown_tx_rejected" + , path=scenario_navigator.screenshot_path + , screen_change_before_first_instruction=True + , screen_change_after_last_instruction=False + ) + else: + # Dismiss the "Enable Blind signing" screen + navigator.navigate([NavInsID.USE_CASE_CHOICE_REJECT], + screen_change_before_first_instruction=False, + screen_change_after_last_instruction=False) + + def check_result(result): + pytest.fail('should not happen') + + with pytest.raises(ExceptionRAPDU) as e: + run_apdu_and_nav_tasks_concurrently(apdu_task, nav_task, check_result) + + assert len(e.value.data) == 0 + +@contextmanager +def blind_sign_enabled(firmware, navigator): + toggle_blind_sign(firmware, navigator) + try: + yield + except: + # Don't re-enable if we hit an exception + raise + else: + toggle_blind_sign(firmware, navigator) + +def toggle_blind_sign(firmware, navigator): + if firmware.device.startswith("nano"): + navigator.navigate( + instructions=[NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, NavInsID.BOTH_CLICK, NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, NavInsID.LEFT_CLICK, NavInsID.LEFT_CLICK] + , timeout=10 + , screen_change_before_first_instruction=False + ) + else: + navigator.navigate([NavInsID.USE_CASE_HOME_SETTINGS, + NavIns(NavInsID.TOUCH, (200, 113)), + NavInsID.USE_CASE_SUB_SETTINGS_EXIT], + timeout=10, + screen_change_before_first_instruction=False, + screen_change_after_last_instruction=False) diff --git a/ragger-tests/test_version_cmd.py b/ragger-tests/test_version_cmd.py new file mode 100644 index 00000000..4f9def0d --- /dev/null +++ b/ragger-tests/test_version_cmd.py @@ -0,0 +1,23 @@ +import tomli +from pathlib import Path +from application_client.client import Client + +# In this test we check the behavior of the device when asked to provide the app version +def test_version(backend): + cargo_path = Path("./rust-app/Cargo.toml") + + if not cargo_path.exists(): + cargo_path = Path("./Cargo.toml") + + if not cargo_path.exists(): + raise FileNotFoundError("Cargo.toml not found") + + with open(cargo_path, "rb") as f: + data = tomli.load(f) + + version = (tuple(map(int, data['package']['version'].split('.'))), "sui") + # Use the app interface instead of raw interface + client = Client(backend, use_block_protocol=True) + # Send the GET_VERSION instruction + response = client.get_app_and_version() + assert response == (version) diff --git a/ragger-tests/utils.py b/ragger-tests/utils.py new file mode 100644 index 00000000..e629be00 --- /dev/null +++ b/ragger-tests/utils.py @@ -0,0 +1,62 @@ +import pytest +import concurrent.futures +import time + +from pathlib import Path +from hashlib import blake2b +from hashlib import sha256 + +from ecdsa.curves import Ed25519 +from ecdsa.keys import VerifyingKey + + +ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() + + +# Check if a signature of a given message is valid +def check_signature_validity(public_key: bytes, signature: bytes, message: bytes) -> bool: + pk: VerifyingKey = VerifyingKey.from_string( + public_key, + curve=Ed25519, + ) + hash_object = blake2b(digest_size=32) + hash_object.update(message) + return pk.verify( + signature=signature, + data=hash_object.digest() + ) + +# Run APDU and navigation tasks concurrently +def run_apdu_and_nav_tasks_concurrently(apdu_task, nav_task, check_result): + executor = concurrent.futures.ThreadPoolExecutor(max_workers=2) + future_apdu = executor.submit(apdu_task) + + # Submit nav_task after a delay + future_nav = executor.submit(lambda: time.sleep(2) or nav_task()) + + try: + # Wait for both futures to complete + done, not_done = concurrent.futures.wait([future_apdu, future_nav], timeout=30, return_when=concurrent.futures.FIRST_EXCEPTION) + print("DEBUG: run_apdu_and_nav_tasks_concurrently, after wait") + + # Check if apdu_task completed successfully + if future_apdu.done(): + result = future_apdu.result() + check_result(result) + else: + for future in done: + try: + future.result() + except Exception as e: + raise + + except concurrent.futures.TimeoutError: + print("DEBUG: run_apdu_and_nav_tasks_concurrently, TimeoutError") + # Cancel both tasks + future_apdu.cancel() + future_nav.cancel() + pytest.fail("Timeout") + + except Exception as e: + print("DEBUG: run_apdu_and_nav_tasks_concurrently, Exception") + raise diff --git a/run-docker-build.sh b/run-docker-build.sh index ebb0fd67..57ec86c2 100755 --- a/run-docker-build.sh +++ b/run-docker-build.sh @@ -2,11 +2,9 @@ set -eu export APP_NAME=`grep name rust-app/Cargo.toml | cut -d '"' -f2 | head -n1` -export RUST_NANOS_SDK_REV="610a73b500730b28c0b8c1b556e089a6b102d7c6" -export RUST_NANOS_SDK_GIT="https://github.com/LedgerHQ/ledger-device-rust-sdk.git" OUT_DIR="./docker-outputs" -for device in nanos nanosplus nanox +for device in nanosplus nanox flex stax do mkdir -p $OUT_DIR/$device done @@ -14,30 +12,22 @@ done # Build apps using nightly docker run \ --env APP_NAME \ - --env RUST_NANOS_SDK_REV \ - --env RUST_NANOS_SDK_GIT \ --env HOST_UID=$(id -u) \ --env HOST_GID=$(id -g) \ --rm -ti -v "$(realpath .):/app" \ - ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest \ + ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest \ docker/run-build-in-docker.sh -# Run tests -# The speculos-wrapper need to be invoked from a dir further down, as it refers to "../ts-tests" -nix-shell -A nanos.rustShell --run "cd $OUT_DIR; ../speculos-wrapper -m nanos ../$OUT_DIR/nanos/$APP_NAME" -nix-shell -A nanosplus.rustShell --run "cd $OUT_DIR; ../speculos-wrapper -m nanosp ../$OUT_DIR/nanosplus/$APP_NAME" -nix-shell -A nanox.rustShell --run "cd $OUT_DIR; ../speculos-wrapper -m nanox ../$OUT_DIR/nanox/$APP_NAME" - -echo "Tests done!" - # Create app.hex -for device in nanos nanosplus nanox +for device in nanosplus nanox flex stax do cp rust-app/Cargo.toml $OUT_DIR/$device/ cp rust-app/*.gif $OUT_DIR/$device/ - nix-shell -A alamgu.perDevice.$device.rustShell --run "cd $OUT_DIR/$device; cargo ledger --use-prebuilt $APP_NAME --hex-next-to-json build $device" + nix-shell -A alamgu.perDevice.$device.rustShell --run "cd $OUT_DIR/$device; cargo ledger --use-prebuilt $APP_NAME build $device" done echo "Use the following commands to install app" -echo 'nix-shell -A alamgu.rustShell --run "cd docker-outputs/nanos; ledgerctl install -f app_nanos.json"' +echo 'nix-shell -A alamgu.rustShell --run "cd docker-outputs/nanox; ledgerctl install -f app_nanox.json"' echo 'nix-shell -A alamgu.rustShell --run "cd docker-outputs/nanosplus; ledgerctl install -f app_nanosplus.json"' +echo 'nix-shell -A alamgu.rustShell --run "cd docker-outputs/flex; ledgerctl install -f app_flex.json"' +echo 'nix-shell -A alamgu.rustShell --run "cd docker-outputs/stax; ledgerctl install -f app_stax.json"' diff --git a/run-ragger-tests.sh b/run-ragger-tests.sh new file mode 100755 index 00000000..9e5ad649 --- /dev/null +++ b/run-ragger-tests.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -eu + +for device in nanosplus nanox flex stax +do + export DEVICE=$device + export pytest_args="$@" + nix-shell -A $DEVICE.rustShell --run " \ + set -x + cd rust-app; \ + cargo build --release --target=\$TARGET_JSON; \ + cd ..; \ + pytest ragger-tests --tb=short -v --device ${DEVICE/nanosplus/nanosp} ${pytest_args}; + " +done diff --git a/rust-app/Cargo.lock b/rust-app/Cargo.lock index cfa82160..0bfcc8b9 100644 --- a/rust-app/Cargo.lock +++ b/rust-app/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "regex 0.1.80", ] +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aho-corasick" version = "0.5.3" @@ -32,7 +38,7 @@ dependencies = [ [[package]] name = "alamgu-async-block" version = "0.1.2" -source = "git+https://github.com/alamgu/alamgu-async-block#bd8c9abe1cd8f3a952e1b95b4adec4039874df10" +source = "git+https://github.com/alamgu/alamgu-async-block#e3d6b773066dd6de1ddfe07924aeed0d0f5a6b0e" dependencies = [ "arrayvec", "ledger-log", @@ -83,6 +89,12 @@ dependencies = [ "which", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -101,6 +113,18 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd769563b4ea2953e2825c9e6b7470a5f55f67e0be00030bf3e390a2a6071f64" +[[package]] +name = "bytemuck" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cc" version = "1.0.78" @@ -139,12 +163,74 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "const-zero" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3c6565524986fe3225da0beb9b4aa55ebc73cd57ff8cb4ccf016ca4c8d006af" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "embedded-alloc" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddae17915accbac2cfbc64ea0ae6e3b330e6ea124ba108dada63646fd3c6f815" +dependencies = [ + "critical-section", + "linked_list_allocator", +] + [[package]] name = "enum-init" version = "0.1.0" @@ -166,6 +252,40 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "exr" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -178,9 +298,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.11.4" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" dependencies = [ "color_quant", "weezl", @@ -192,6 +312,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "home" version = "0.5.9" @@ -202,15 +332,43 @@ dependencies = [ ] [[package]] -name = "include_gif" -version = "1.0.1" +name = "image" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "132290d08a42868f8f90e96a9206e955b0aae1e5a5df54c0029e8c2ab8652625" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", "gif", + "jpeg-decoder", + "num-traits", + "png", + "qoi", + "tiff", +] + +[[package]] +name = "include_gif" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8511434ad4b61bf0be7c61707d0172b6ad091519da93bf95d66555f3f0d994ac" +dependencies = [ + "flate2", + "image", "syn 1.0.107", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +dependencies = [ + "rayon", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -233,6 +391,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "ledger-crypto-helpers" version = "0.2.1" @@ -258,7 +422,7 @@ dependencies = [ [[package]] name = "ledger-parser-combinators" version = "0.1.2" -source = "git+https://github.com/alamgu/ledger-parser-combinators?branch=async-split-take-2#90662ff6e74ddb280fae3a145815bec12ad38120" +source = "git+https://github.com/alamgu/ledger-parser-combinators?branch=async-split-take-2#f44ef969d0c5837c82db002d4660df490445ed2e" dependencies = [ "arrayvec", "bstringify", @@ -277,7 +441,7 @@ dependencies = [ [[package]] name = "ledger-prompts-ui" version = "0.1.0" -source = "git+https://github.com/alamgu/ledger-prompts-ui#b3854fb0110803ba91fe0dc466f883ffef458177" +source = "git+https://github.com/alamgu/ledger-prompts-ui#9f851a336b248e7458916e8deccf793bea73faa7" dependencies = [ "arrayvec", "include_gif", @@ -287,10 +451,11 @@ dependencies = [ [[package]] name = "ledger_device_sdk" -version = "1.7.1" +version = "1.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c119b704c1240c3e21fcaff94ea8cdc110ac949eb118c5e6e5dbc9c7a61ebe2" +checksum = "86980cdfca74b94db326557e3c6246c7a7c63331aa2a96f193f826cd04613fd5" dependencies = [ + "const-zero", "include_gif", "ledger_secure_sdk_sys", "num-traits", @@ -301,12 +466,15 @@ dependencies = [ [[package]] name = "ledger_secure_sdk_sys" -version = "1.2.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60e201ddad57baebaf48c694341d1e0aa910d3516cafee32fc385fa73c01381c" +checksum = "b046c4ef3859a4e693bd6a49b2e9ee3295d7bf5c3ae7b6bb5cd8b358b1a6cb61" dependencies = [ "bindgen", "cc", + "critical-section", + "embedded-alloc", + "glob", ] [[package]] @@ -325,6 +493,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -358,6 +532,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "nom" version = "7.1.3" @@ -458,6 +642,19 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "png" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "prettyplease" version = "0.2.16" @@ -477,6 +674,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quote" version = "1.0.35" @@ -492,6 +698,26 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "0.1.80" @@ -565,9 +791,21 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "sui" -version = "0.2.1" +version = "0.2.2" dependencies = [ "alamgu-async-block", "arrayvec", @@ -625,6 +863,17 @@ dependencies = [ "thread-id", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "trie-enum" version = "0.1.0" @@ -659,9 +908,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "weezl" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "which" @@ -824,3 +1073,12 @@ name = "zeroize" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/rust-app/Cargo.toml b/rust-app/Cargo.toml index 1d8b21ed..67435e8d 100644 --- a/rust-app/Cargo.toml +++ b/rust-app/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sui" -version = "0.2.1" -authors = ["Ericson2314", "dfordivam", "dbzguy56"] +version = "0.2.2" +authors = ["Obsidian Systems"] edition = "2018" autobins = false resolver = "2" @@ -25,14 +25,17 @@ default = [ ] # Pass --features speculos,ledger-log/log_info (or log_trace, etc) to enable speculos logging and change log level. speculos = [ "ledger_device_sdk/speculos", "ledger-log/speculos", "ledger-log/log_error", "ledger-parser-combinators/logging" ] extra_debug = ["ledger-log/log_trace"] +pending_review_screen = [] [target.'cfg(target_family = "bolos")'.dependencies] -ledger_device_sdk = "1.7.1" -ledger_secure_sdk_sys = "1.2.0" +ledger_device_sdk = "1.19.1" +ledger_secure_sdk_sys = "1.6.1" + +[target.'cfg(any(target_os = "nanosplus", target_os = "nanox"))'.dependencies] ledger-prompts-ui = { git = "https://github.com/alamgu/ledger-prompts-ui" } [target.'cfg(target_family = "bolos")'.dev-dependencies.ledger_device_sdk] -version = "1.7.1" +version = "1.19.1" features = [ "speculos" ] [[bin]] @@ -50,17 +53,23 @@ overflow-checks = false opt-level = 3 overflow-checks = false +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("nanos", "flex", "stax"))'] } + [package.metadata.ledger] name = "Sui" path = ["44'/784'"] curve = ["ed25519"] flags = "0" -[package.metadata.ledger.nanos] -icon = "sui.gif" - [package.metadata.ledger.nanox] icon = "sui-small.gif" [package.metadata.ledger.nanosplus] icon = "sui-small.gif" + +[package.metadata.ledger.stax] +icon = "sui_32x32.gif" + +[package.metadata.ledger.flex] +icon = "sui_40x40.gif" diff --git a/rust-app/bin-src/main.rs b/rust-app/bin-src/main.rs index d687bc86..667e4900 100644 --- a/rust-app/bin-src/main.rs +++ b/rust-app/bin-src/main.rs @@ -4,8 +4,12 @@ #[cfg(not(target_family = "bolos"))] fn main() {} +#[cfg(not(any(target_os = "stax", target_os = "flex")))] use sui::main_nanos::*; +#[cfg(any(target_os = "stax", target_os = "flex"))] +use sui::main_stax::*; + ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); #[no_mangle] diff --git a/rust-app/build.rs b/rust-app/build.rs index 6d36ea80..a3595891 100644 --- a/rust-app/build.rs +++ b/rust-app/build.rs @@ -1,3 +1,8 @@ +use std::env; + fn main() { println!("cargo:rerun-if-changed=script.ld"); + if let Ok(path) = env::var("NEWLIB_LIB_PATH") { + println!("cargo:rustc-link-search={path}"); + } } diff --git a/rust-app/src/handle_apdu.rs b/rust-app/src/handle_apdu.rs new file mode 100644 index 00000000..dca8bca0 --- /dev/null +++ b/rust-app/src/handle_apdu.rs @@ -0,0 +1,65 @@ +use crate::implementation::*; +use crate::interface::*; +use crate::settings::*; +use crate::ui::UserInterface; +use crate::utils::*; + +use alamgu_async_block::*; +use arrayvec::ArrayVec; +use core::future::Future; +use ledger_log::trace; + +pub type APDUsFuture = impl Future; + +#[inline(never)] +pub fn handle_apdu_async( + io: HostIO, + ins: Ins, + settings: Settings, + ui: UserInterface, +) -> APDUsFuture { + trace!("Constructing future"); + async move { + trace!("Dispatching"); + match ins { + Ins::GetVersion => { + const APP_NAME: &str = "sui"; + let mut rv = ArrayVec::::new(); + let _ = rv.try_push(env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap()); + let _ = rv.try_push(env!("CARGO_PKG_VERSION_MINOR").parse().unwrap()); + let _ = rv.try_push(env!("CARGO_PKG_VERSION_PATCH").parse().unwrap()); + let _ = rv.try_extend_from_slice(APP_NAME.as_bytes()); + io.result_final(&rv).await; + } + Ins::VerifyAddress => { + NoinlineFut(get_address_apdu(io, ui, true)).await; + } + Ins::GetPubkey => { + NoinlineFut(get_address_apdu(io, ui, false)).await; + } + Ins::Sign => { + trace!("Handling sign"); + NoinlineFut(sign_apdu(io, settings, ui)).await; + } + Ins::GetVersionStr => {} + Ins::Exit => ledger_device_sdk::exit_app(0), + } + } +} + +// We are single-threaded in fact, albeit with nontrivial code flow. We don't need to worry about +// full atomicity of the below globals. +pub struct SingleThreaded(pub T); +unsafe impl Send for SingleThreaded {} +unsafe impl Sync for SingleThreaded {} +impl core::ops::Deref for SingleThreaded { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } +} +impl core::ops::DerefMut for SingleThreaded { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } +} diff --git a/rust-app/src/implementation.rs b/rust-app/src/implementation.rs index 70c34298..d1f8da90 100644 --- a/rust-app/src/implementation.rs +++ b/rust-app/src/implementation.rs @@ -1,11 +1,10 @@ use crate::interface::*; use crate::settings::*; +use crate::ui::*; use crate::utils::*; use alamgu_async_block::*; -use arrayvec::ArrayString; use arrayvec::ArrayVec; -use core::fmt::Write; -use ledger_crypto_helpers::common::{try_option, Address, HexSlice}; +use ledger_crypto_helpers::common::{try_option, Address}; use ledger_crypto_helpers::eddsa::{ed25519_public_key_bytes, eddsa_sign, with_public_keys}; use ledger_crypto_helpers::hasher::{Blake2b, Hasher, HexHash}; use ledger_device_sdk::io::{StatusWords, SyscallError}; @@ -13,48 +12,18 @@ use ledger_log::trace; use ledger_parser_combinators::async_parser::*; use ledger_parser_combinators::bcs::async_parser::*; use ledger_parser_combinators::interp::*; -use ledger_prompts_ui::{final_accept_prompt, ScrollerError}; use core::convert::TryFrom; use core::future::Future; -type SuiAddressRaw = [u8; SUI_ADDRESS_LENGTH]; - -pub struct SuiPubKeyAddress(ledger_device_sdk::ecc::ECPublicKey<65, 'E'>, SuiAddressRaw); - -impl Address> for SuiPubKeyAddress { - fn get_address( - key: &ledger_device_sdk::ecc::ECPublicKey<65, 'E'>, - ) -> Result { - let key_bytes = ed25519_public_key_bytes(key); - let mut tmp = ArrayVec::::new(); - let _ = tmp.try_push(0); // SIGNATURE_SCHEME_TO_FLAG['ED25519'] - let _ = tmp.try_extend_from_slice(key_bytes); - let mut hasher: Blake2b = Hasher::new(); - hasher.update(&tmp); - let hash: [u8; SUI_ADDRESS_LENGTH] = hasher.finalize(); - Ok(SuiPubKeyAddress(key.clone(), hash)) - } - fn get_binary_address(&self) -> &[u8] { - &self.1 - } -} - -impl core::fmt::Display for SuiPubKeyAddress { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "0x{}", HexSlice(&self.1)) - } -} - -pub type BipParserImplT = - impl AsyncParser + HasOutput>; +pub type BipParserImplT = impl AsyncParser>; pub const BIP_PATH_PARSER: BipParserImplT = SubInterp(DefaultInterp); // Need a path of length 5, as make_bip32_path panics with smaller paths pub const BIP32_PREFIX: [u32; 5] = ledger_device_sdk::ecc::make_bip32_path(b"m/44'/784'/123'/0'/0'"); -pub async fn get_address_apdu(io: HostIO, prompt: bool) { +pub async fn get_address_apdu(io: HostIO, ui: UserInterface, prompt: bool) { let input = match io.get_params::<1>() { Some(v) => v, None => reject(SyscallError::InvalidParameter as u16).await, @@ -71,9 +40,7 @@ pub async fn get_address_apdu(io: HostIO, prompt: bool) { if with_public_keys(&path, true, |key, address: &SuiPubKeyAddress| { try_option(|| -> Option<()> { if prompt { - scroller("Provide Public Key", |_w| Ok(()))?; - scroller_paginated("Address", |w| Ok(write!(w, "{address}")?))?; - final_accept_prompt(&[])?; + ui.confirm_address(address)?; } let key_bytes = ed25519_public_key_bytes(key); @@ -108,7 +75,10 @@ impl HasOutput for DefaultInterp { } impl AsyncParser for DefaultInterp { - type State<'c> = impl Future + 'c where BS: 'c; + type State<'c> + = impl Future + 'c + where + BS: 'c; fn parse<'a: 'c, 'b: 'c, 'c>(&'b self, input: &'a mut BS) -> Self::State<'c> { async move { let enum_variant = @@ -199,7 +169,10 @@ impl HasOutput for DefaultInterp { } impl AsyncParser for DefaultInterp { - type State<'c> = impl Future + 'c where BS: 'c; + type State<'c> + = impl Future + 'c + where + BS: 'c; fn parse<'a: 'c, 'b: 'c, 'c>(&'b self, input: &'a mut BS) -> Self::State<'c> { async move { let enum_variant = @@ -259,7 +232,10 @@ impl HasOutput for DefaultInterp { } impl AsyncParser for DefaultInterp { - type State<'c> = impl Future + 'c where BS: 'c; + type State<'c> + = impl Future + 'c + where + BS: 'c; fn parse<'a: 'c, 'b: 'c, 'c>(&'b self, input: &'a mut BS) -> Self::State<'c> { async move { let enum_variant = @@ -305,19 +281,21 @@ impl AsyncParser for DefaultInterp { } } -impl HasOutput> - for ProgrammableTransaction -{ - type Output = (); +impl HasOutput for ProgrammableTransaction { + type Output = ( + >::Output, + >::Output, + ); } -impl AsyncParser, BS> - for ProgrammableTransaction -{ - type State<'c> = impl Future + 'c where BS: 'c; +impl AsyncParser for ProgrammableTransaction { + type State<'c> + = impl Future + 'c + where + BS: 'c; fn parse<'a: 'c, 'b: 'c, 'c>(&'b self, input: &'a mut BS) -> Self::State<'c> { async move { - let mut recipient = None; + let mut recipient_addr = None; let mut recipient_index = None; let mut amounts: ArrayVec<(u64, u32), SPLIT_COIN_ARRAY_LENGTH> = ArrayVec::new(); @@ -334,9 +312,9 @@ impl AsyncParser match recipient { + CallArg::RecipientAddress(addr) => match recipient_addr { None => { - recipient = Some(addr); + recipient_addr = Some(addr); recipient_index = Some(i); } // Reject on multiple RecipientAddress(s) @@ -378,6 +356,18 @@ impl AsyncParser addr, + _ => { + reject_on( + core::file!(), + core::line!(), + SyscallError::NotSupported as u16, + ) + .await + } + }; + let mut verified_recipient = false; let mut total_amount: u64 = 0; // Handle commands @@ -480,38 +470,20 @@ impl AsyncParser::is_none( - &try { - scroller_paginated("To", |w| { - Ok(write!( - w, - "0x{}", - HexSlice(&recipient.ok_or(ScrollerError)?) - )?) - })?; - - let (quotient, remainder_str) = get_amount_in_decimals(total_amount); - scroller_paginated("Amount", |w| { - Ok(write!(w, "SUI {quotient}.{}", remainder_str.as_str())?) - })?; - }, - ) - { - reject::<()>(StatusWords::UserCancelled as u16).await; - } + (recipient, total_amount) } } } -impl HasOutput> for TransactionKind { - type Output = (); +impl HasOutput for TransactionKind { + type Output = >::Output; } -impl AsyncParser, BS> - for TransactionKind -{ - type State<'c> = impl Future + 'c where BS: 'c; +impl AsyncParser for TransactionKind { + type State<'c> + = impl Future + 'c + where + BS: 'c; fn parse<'a: 'c, 'b: 'c, 'c>(&'b self, input: &'a mut BS) -> Self::State<'c> { async move { let enum_variant = @@ -519,11 +491,11 @@ impl AsyncParser { trace!("TransactionKind: ProgrammableTransaction"); - as AsyncParser< - ProgrammableTransaction, - BS, - >>::parse(&ProgrammableTransaction::, input) - .await; + >::parse( + &ProgrammableTransaction, + input, + ) + .await } _ => { trace!("TransactionKind: {}", enum_variant); @@ -539,35 +511,15 @@ impl AsyncParser (u64, ArrayString<12>) { - let factor_pow = 9; - let factor = u64::pow(10, factor_pow); - let quotient = amount / factor; - let remainder = amount % factor; - let mut remainder_str: ArrayString<12> = ArrayString::new(); - { - // Make a string for the remainder, containing at lease one zero - // So 1 SUI will be displayed as "1.0" - let mut rem = remainder; - for i in 0..factor_pow { - let f = u64::pow(10, factor_pow - i - 1); - let r = rem / f; - let _ = remainder_str.try_push(char::from(b'0' + r as u8)); - rem %= f; - if rem == 0 { - break; - } - } - } - (quotient, remainder_str) -} - impl HasOutput for DefaultInterp { type Output = (); } impl AsyncParser for DefaultInterp { - type State<'c> = impl Future + 'c where BS: 'c; + type State<'c> + = impl Future + 'c + where + BS: 'c; fn parse<'a: 'c, 'b: 'c, 'c>(&'b self, input: &'a mut BS) -> Self::State<'c> { async move { let enum_variant = @@ -593,8 +545,7 @@ impl AsyncParser for DefaultInt } } -const fn gas_data_parser( -) -> impl AsyncParser, BS> + HasOutput, Output = ()> { +const fn gas_data_parser() -> impl AsyncParser { Action( ( SubInterp(object_ref_parser()), @@ -608,52 +559,46 @@ const fn gas_data_parser( // just ignore that field. // // C.F. https://github.com/MystenLabs/sui/pull/8676 - if PROMPT { - let (quotient, remainder_str) = get_amount_in_decimals(gas_budget); - scroller("Max Gas", |w| { - Ok(write!(w, "SUI {}.{}", quotient, remainder_str.as_str())?) - })? - } - Some(()) + Some(gas_budget) }, ) } -const fn object_ref_parser( -) -> impl AsyncParser + HasOutput { +const fn object_ref_parser() -> impl AsyncParser { Action((DefaultInterp, DefaultInterp, DefaultInterp), |_| Some(())) } -const fn intent_parser( -) -> impl AsyncParser + HasOutput { +const fn intent_parser() -> impl AsyncParser { Action((DefaultInterp, DefaultInterp, DefaultInterp), |_| { trace!("Intent Ok"); Some(()) }) } -const fn transaction_data_v1_parser( -) -> impl AsyncParser, BS> + HasOutput, Output = ()> -{ +type TransactionDataV1Output = (>::Output, u64); + +const fn transaction_data_v1_parser( +) -> impl AsyncParser { Action( ( - TransactionKind::, + TransactionKind, DefaultInterp, - gas_data_parser::<_, PROMPT>(), + gas_data_parser(), DefaultInterp, ), - |_| Some(()), + |(v, _, gas_budget, _)| Some((v, gas_budget)), ) } -impl HasOutput> for TransactionData { - type Output = (); +impl HasOutput for TransactionData { + type Output = TransactionDataV1Output; } -impl AsyncParser, BS> - for TransactionData -{ - type State<'c> = impl Future + 'c where BS: 'c; +impl AsyncParser for TransactionData { + type State<'c> + = impl Future + 'c + where + BS: 'c; fn parse<'a: 'c, 'b: 'c, 'c>(&'b self, input: &'a mut BS) -> Self::State<'c> { async move { let enum_variant = @@ -661,7 +606,7 @@ impl AsyncParser { trace!("TransactionData: V1"); - transaction_data_v1_parser::<_, PROMPT>().parse(input).await; + transaction_data_v1_parser().parse(input).await } _ => { reject_on( @@ -676,12 +621,13 @@ impl AsyncParser( -) -> impl AsyncParser, BS> + HasOutput, Output = ()> { - Action((intent_parser(), TransactionData::), |_| Some(())) +const fn tx_parser( +) -> impl AsyncParser>::Output> +{ + Action((intent_parser(), TransactionData), |(_, d)| Some(d)) } -pub async fn sign_apdu(io: HostIO, settings: Settings) { +pub async fn sign_apdu(io: HostIO, settings: Settings, ui: UserInterface) { let mut input = match io.get_params::<2>() { Some(v) => v, None => reject(SyscallError::InvalidParameter as u16).await, @@ -694,64 +640,35 @@ pub async fn sign_apdu(io: HostIO, settings: Settings) { let mut txn = input[0].clone(); NoinlineFut(async move { trace!("Beginning check parse"); - TryFuture(tx_parser::<_, false>().parse(&mut txn)) - .await - .is_some() + TryFuture(tx_parser().parse(&mut txn)).await.is_some() }) .await }; if known_txn { - if scroller("Transfer", |w| Ok(write!(w, "SUI")?)).is_none() { - reject::<()>(StatusWords::UserCancelled as u16).await; - }; - { - let mut bs = input[1].clone(); - NoinlineFut(async move { - let path = BIP_PATH_PARSER.parse(&mut bs).await; - if !path.starts_with(&BIP32_PREFIX[0..2]) { - reject::<()>(SyscallError::InvalidParameter as u16).await; - } - if with_public_keys(&path, true, |_, address: &SuiPubKeyAddress| { - try_option(|| -> Option<()> { - scroller_paginated("From", |w| Ok(write!(w, "{address}")?))?; - Some(()) - }()) - }) - .ok() - .is_none() - { - reject::<()>(StatusWords::UserCancelled as u16).await; - } - }) - .await - }; + let mut txn = input[0].clone(); + let ((recipient, total_amount), gas_budget) = tx_parser().parse(&mut txn).await; - { - let mut txn = input[0].clone(); - NoinlineFut(async move { - trace!("Beginning parse"); - tx_parser::<_, true>().parse(&mut txn).await; - }) - .await - }; + let mut bs = input[1].clone(); + let path = BIP_PATH_PARSER.parse(&mut bs).await; + if !path.starts_with(&BIP32_PREFIX[0..2]) { + reject::<()>(SyscallError::InvalidParameter as u16).await; + } - if final_accept_prompt(&["Sign Transaction?"]).is_none() { + // Show prompts after all inputs have been parsed + if with_public_keys(&path, true, |_, address: &SuiPubKeyAddress| { + try_option(ui.confirm_sign_tx(address, recipient, total_amount, gas_budget)) + }) + .ok() + .is_none() + { reject::<()>(StatusWords::UserCancelled as u16).await; }; - } else if settings.get() == 0 { - scroller("WARNING", |w| { - Ok(write!( - w, - "Transaction not recognized, enable blind signing to sign unknown transactions" - )?) - }); + } else if !settings.get_blind_sign() { + ui.warn_tx_not_recognized(); reject::<()>(SyscallError::NotSupported as u16).await; - } else if scroller("WARNING", |w| Ok(write!(w, "Transaction not recognized")?)).is_none() { - reject::<()>(StatusWords::UserCancelled as u16).await; } - // By the time we get here, we've approved and just need to do the signature. NoinlineFut(async move { let mut hasher: Blake2b = Hasher::new(); { @@ -769,10 +686,8 @@ pub async fn sign_apdu(io: HostIO, settings: Settings) { } let hash: HexHash<32> = hasher.finalize(); if !known_txn { - if scroller("Transaction Hash", |w| Ok(write!(w, "0x{hash}")?)).is_none() { - reject::<()>(StatusWords::UserCancelled as u16).await; - }; - if final_accept_prompt(&["Blind Sign Transaction?"]).is_none() { + // Show prompts after all inputs have been parsed + if ui.confirm_blind_sign_tx(&hash).is_none() { reject::<()>(StatusWords::UserCancelled as u16).await; }; } @@ -788,36 +703,3 @@ pub async fn sign_apdu(io: HostIO, settings: Settings) { }) .await } - -pub type APDUsFuture = impl Future; - -#[inline(never)] -pub fn handle_apdu_async(io: HostIO, ins: Ins, settings: Settings) -> APDUsFuture { - trace!("Constructing future"); - async move { - trace!("Dispatching"); - match ins { - Ins::GetVersion => { - const APP_NAME: &str = "sui"; - let mut rv = ArrayVec::::new(); - let _ = rv.try_push(env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap()); - let _ = rv.try_push(env!("CARGO_PKG_VERSION_MINOR").parse().unwrap()); - let _ = rv.try_push(env!("CARGO_PKG_VERSION_PATCH").parse().unwrap()); - let _ = rv.try_extend_from_slice(APP_NAME.as_bytes()); - io.result_final(&rv).await; - } - Ins::VerifyAddress => { - NoinlineFut(get_address_apdu(io, true)).await; - } - Ins::GetPubkey => { - NoinlineFut(get_address_apdu(io, false)).await; - } - Ins::Sign => { - trace!("Handling sign"); - NoinlineFut(sign_apdu(io, settings)).await; - } - Ins::GetVersionStr => {} - Ins::Exit => ledger_device_sdk::exit_app(0), - } - } -} diff --git a/rust-app/src/interface.rs b/rust-app/src/interface.rs index 1d0df558..cad12907 100644 --- a/rust-app/src/interface.rs +++ b/rust-app/src/interface.rs @@ -8,29 +8,29 @@ use num_enum::TryFromPrimitive; // Payload for a public key request pub type Bip32Key = DArray, 10>; -pub type SignParameters = (IntentMessage, Bip32Key); +pub type SignParameters = (IntentMessage, Bip32Key); // Sui Types -pub type IntentMessage = (Intent, TransactionData); +pub type IntentMessage = (Intent, TransactionData); -pub struct TransactionData; +pub struct TransactionData; -pub type TransactionDataV1 = ( - TransactionKind, +pub type TransactionDataV1 = ( + TransactionKind, SuiAddress, // sender - GasData, // gas_data + GasData, // gas_data TransactionExpiration, // expiration ); -pub struct TransactionKind; +pub struct TransactionKind; -pub struct ProgrammableTransaction; +pub struct ProgrammableTransaction; pub struct CommandSchema; pub struct ArgumentSchema; pub struct CallArgSchema; -pub type GasData = ( +pub type GasData = ( Vec, // payment SuiAddress, // owner Amount, // price @@ -74,6 +74,41 @@ pub type AppId = ULEB128; #[allow(non_camel_case_types)] pub type SHA3_256_HASH = Array; +pub type SuiAddressRaw = [u8; SUI_ADDRESS_LENGTH]; + +#[allow(dead_code)] +pub struct SuiPubKeyAddress(ledger_device_sdk::ecc::ECPublicKey<65, 'E'>, SuiAddressRaw); + +use arrayvec::ArrayVec; +use ledger_crypto_helpers::common::{Address, HexSlice}; +use ledger_crypto_helpers::eddsa::ed25519_public_key_bytes; +use ledger_crypto_helpers::hasher::{Blake2b, Hasher}; +use ledger_device_sdk::io::SyscallError; + +impl Address> for SuiPubKeyAddress { + fn get_address( + key: &ledger_device_sdk::ecc::ECPublicKey<65, 'E'>, + ) -> Result { + let key_bytes = ed25519_public_key_bytes(key); + let mut tmp = ArrayVec::::new(); + let _ = tmp.try_push(0); // SIGNATURE_SCHEME_TO_FLAG['ED25519'] + let _ = tmp.try_extend_from_slice(key_bytes); + let mut hasher: Blake2b = Hasher::new(); + hasher.update(&tmp); + let hash: [u8; SUI_ADDRESS_LENGTH] = hasher.finalize(); + Ok(SuiPubKeyAddress(key.clone(), hash)) + } + fn get_binary_address(&self) -> &[u8] { + &self.1 + } +} + +impl core::fmt::Display for SuiPubKeyAddress { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "0x{}", HexSlice(&self.1)) + } +} + #[repr(u8)] #[derive(Debug, TryFromPrimitive)] pub enum Ins { diff --git a/rust-app/src/lib.rs b/rust-app/src/lib.rs index 6a075652..84bbf4d3 100644 --- a/rust-app/src/lib.rs +++ b/rust-app/src/lib.rs @@ -2,9 +2,10 @@ #![allow(incomplete_features)] #![feature(stmt_expr_attributes)] #![feature(adt_const_params)] -#![feature(str_internals)] #![feature(type_alias_impl_trait)] -#![feature(const_mut_refs)] +#![cfg_attr(not(version("1.83")), feature(const_mut_refs))] +// Hack to fix build with nightly-2024-11-22 +#![cfg_attr(version("1.84"), feature(generic_const_exprs))] #![feature(try_blocks)] #![cfg_attr(all(target_family = "bolos", test), no_main)] #![cfg_attr(target_family = "bolos", feature(custom_test_frameworks))] @@ -35,18 +36,30 @@ pub mod interface; #[cfg(target_family = "bolos")] pub mod utils; +#[cfg(target_family = "bolos")] +pub mod handle_apdu; + #[cfg(target_family = "bolos")] pub mod implementation; #[cfg(target_family = "bolos")] +#[cfg(not(any(target_os = "stax", target_os = "flex")))] pub mod menu; #[cfg(target_family = "bolos")] pub mod settings; #[cfg(target_family = "bolos")] +#[cfg(not(any(target_os = "stax", target_os = "flex")))] pub mod main_nanos; +#[cfg(target_family = "bolos")] +pub mod ui; + +#[cfg(target_family = "bolos")] +#[cfg(any(target_os = "stax", target_os = "flex"))] +pub mod main_stax; + #[cfg(all(target_family = "bolos", test))] use core::panic::PanicInfo; /// In case of runtime problems, return an internal error and exit the app diff --git a/rust-app/src/main_nanos.rs b/rust-app/src/main_nanos.rs index b343309b..3cbb1326 100644 --- a/rust-app/src/main_nanos.rs +++ b/rust-app/src/main_nanos.rs @@ -1,7 +1,8 @@ -use crate::implementation::*; +use crate::handle_apdu::*; use crate::interface::*; use crate::menu::*; use crate::settings::*; +use crate::ui::UserInterface; use alamgu_async_block::*; @@ -20,15 +21,25 @@ pub fn app_main() { let hostio_state: SingleThreaded> = SingleThreaded(RefCell::new(HostIOState::new(unsafe { - core::mem::transmute(&comm.0) + core::mem::transmute::< + &core::cell::RefCell, + &core::cell::RefCell, + >(&comm.0) }))); - let hostio: SingleThreaded = - SingleThreaded(HostIO(unsafe { core::mem::transmute(&hostio_state.0) })); + let hostio: SingleThreaded = SingleThreaded(HostIO(unsafe { + core::mem::transmute::< + &core::cell::RefCell, + &core::cell::RefCell, + >(&hostio_state.0) + })); let states_backing: SingleThreaded>> = SingleThreaded(PinCell::new(None)); let states: SingleThreaded>>> = SingleThreaded(Pin::static_ref(unsafe { - core::mem::transmute(&states_backing.0) + core::mem::transmute::< + &pin_cell::PinCell>, + &pin_cell::PinCell>, + >(&states_backing.0) })); let mut idle_menu = IdleMenuWithSettings { @@ -64,7 +75,7 @@ pub fn app_main() { PinMut::as_mut(&mut states.0.borrow_mut()), ins, *hostio, - |io, ins| handle_apdu_async(io, ins, idle_menu.settings), + |io, ins| handle_apdu_async(io, ins, idle_menu.settings, UserInterface {}), ); match poll_rv { Ok(()) => { @@ -114,20 +125,3 @@ pub fn app_main() { } } } - -// We are single-threaded in fact, albeit with nontrivial code flow. We don't need to worry about -// full atomicity of the below globals. -struct SingleThreaded(T); -unsafe impl Send for SingleThreaded {} -unsafe impl Sync for SingleThreaded {} -impl core::ops::Deref for SingleThreaded { - type Target = T; - fn deref(&self) -> &T { - &self.0 - } -} -impl core::ops::DerefMut for SingleThreaded { - fn deref_mut(&mut self) -> &mut T { - &mut self.0 - } -} diff --git a/rust-app/src/main_stax.rs b/rust-app/src/main_stax.rs new file mode 100644 index 00000000..096436b1 --- /dev/null +++ b/rust-app/src/main_stax.rs @@ -0,0 +1,113 @@ +use crate::handle_apdu::*; +use crate::interface::*; +use crate::settings::*; +use crate::ui::{UserInterface, APP_ICON}; + +use alamgu_async_block::*; + +use core::cell::RefCell; +use core::pin::Pin; +use pin_cell::*; + +use ledger_device_sdk::io; +use ledger_device_sdk::nbgl::{init_comm, NbglHomeAndSettings}; +use ledger_log::{info, trace}; + +#[allow(dead_code)] +pub fn app_main() { + let comm: SingleThreaded> = SingleThreaded(RefCell::new(io::Comm::new())); + + let hostio_state: SingleThreaded> = + SingleThreaded(RefCell::new(HostIOState::new(unsafe { + core::mem::transmute::< + &core::cell::RefCell, + &core::cell::RefCell, + >(&comm.0) + }))); + let hostio: SingleThreaded = SingleThreaded(HostIO(unsafe { + core::mem::transmute::< + &core::cell::RefCell, + &core::cell::RefCell, + >(&hostio_state.0) + })); + let states_backing: SingleThreaded>> = + SingleThreaded(PinCell::new(None)); + let states: SingleThreaded>>> = + SingleThreaded(Pin::static_ref(unsafe { + core::mem::transmute::< + &pin_cell::PinCell>, + &pin_cell::PinCell>, + >(&states_backing.0) + })); + + let mut settings = Settings; + + // Initialize reference to Comm instance for NBGL + // API calls. + init_comm(&mut comm.borrow_mut()); + + info!("Sui {}", env!("CARGO_PKG_VERSION")); + info!( + "State sizes\ncomm: {}\nstates: {}", + core::mem::size_of::(), + core::mem::size_of::>() + ); + + let settings_strings = [[ + "Blind Signing", + "Sign transactions for which details cannot be verified", + ]]; + + let main_menu = SingleThreaded(RefCell::new( + NbglHomeAndSettings::new() + .glyph(&APP_ICON) + .settings(settings.get_mut(), &settings_strings) + .infos("Sui", env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_AUTHORS")), + )); + let do_refresh_val = true; + let do_refresh = SingleThreaded(RefCell::new(do_refresh_val)); + let ui = UserInterface { + main_menu: unsafe { + core::mem::transmute::< + &core::cell::RefCell, + &core::cell::RefCell, + >(&main_menu.0) + }, + do_refresh: unsafe { + core::mem::transmute::<&core::cell::RefCell, &core::cell::RefCell>( + &do_refresh.0, + ) + }, + }; + + let menu = |states: core::cell::Ref<'_, Option>| { + if states.is_none() { + ui.show_main_menu() + } + }; + + loop { + // This must be here, before handle_apdu + // somehow doesn't work if its after handle_apdu + menu(states.borrow()); + let ins: Ins = comm.borrow_mut().next_command(); + + let poll_rv = poll_apdu_handlers( + PinMut::as_mut(&mut states.0.borrow_mut()), + ins, + *hostio, + |io, ins| handle_apdu_async(io, ins, settings, ui), + ); + match poll_rv { + Ok(()) => { + trace!("APDU accepted; sending response"); + comm.borrow_mut().reply_ok(); + trace!("Replied"); + } + Err(sw) => { + PinMut::as_mut(&mut states.0.borrow_mut()).set(None); + comm.borrow_mut().reply(sw); + } + }; + } +} diff --git a/rust-app/src/menu.rs b/rust-app/src/menu.rs index 0b6c2e57..22e4790e 100644 --- a/rust-app/src/menu.rs +++ b/rust-app/src/menu.rs @@ -45,7 +45,7 @@ impl Menu for IdleMenuWithSettings { ShowVersion => self.idle_menu = AppMain, Settings(None) => self.idle_menu = ShowVersion, Settings(Some(Back)) => { - if self.settings.get() == 1 { + if self.settings.get_blind_sign() { self.idle_menu = Settings(Some(DisableBlindSigning)) } else { self.idle_menu = Settings(Some(EnableBlindSigning)) @@ -63,7 +63,7 @@ impl Menu for IdleMenuWithSettings { ShowVersion => self.idle_menu = Settings(None), Settings(None) => self.idle_menu = Exit, Settings(Some(Back)) => { - if self.settings.get() == 1 { + if self.settings.get_blind_sign() { self.idle_menu = Settings(Some(DisableBlindSigning)) } else { self.idle_menu = Settings(Some(EnableBlindSigning)) @@ -81,7 +81,7 @@ impl Menu for IdleMenuWithSettings { AppMain => None, ShowVersion => None, Settings(None) => { - if self.settings.get() == 1 { + if self.settings.get_blind_sign() { self.idle_menu = Settings(Some(DisableBlindSigning)) } else { self.idle_menu = Settings(Some(EnableBlindSigning)) @@ -89,12 +89,12 @@ impl Menu for IdleMenuWithSettings { None } Settings(Some(EnableBlindSigning)) => { - self.settings.set(&1); + self.settings.set_blind_sign(true); self.idle_menu = Settings(Some(DisableBlindSigning)); None } Settings(Some(DisableBlindSigning)) => { - self.settings.set(&0); + self.settings.set_blind_sign(false); self.idle_menu = Settings(Some(EnableBlindSigning)); None } diff --git a/rust-app/src/settings.rs b/rust-app/src/settings.rs index 77bb1992..7bc407f0 100644 --- a/rust-app/src/settings.rs +++ b/rust-app/src/settings.rs @@ -2,8 +2,12 @@ use ledger_device_sdk::nvm::*; use ledger_device_sdk::NVMData; // This is necessary to store the object in NVM and not in RAM +const SETTINGS_SIZE: usize = 10; #[link_section = ".nvm_data"] -static mut SETTINGS: NVMData> = NVMData::new(AtomicStorage::new(&0)); +static mut SETTINGS: NVMData> = + NVMData::new(AtomicStorage::new(&[0u8; 10])); + +const BLINDSIGN_IX: usize = 0; #[derive(Clone, Copy)] pub struct Settings; @@ -16,15 +20,31 @@ impl Default for Settings { impl Settings { #[inline(never)] - pub fn get(&self) -> u8 { - let settings = unsafe { SETTINGS.get_mut() }; - return *settings.get_ref(); + pub fn get_mut(&mut self) -> &mut AtomicStorage<[u8; SETTINGS_SIZE]> { + #[allow(static_mut_refs)] + unsafe { + SETTINGS.get_mut() + } + } + + #[inline(never)] + pub fn get_blind_sign(&self) -> bool { + #[allow(static_mut_refs)] + let settings = unsafe { SETTINGS.get_ref() }; + settings.get_ref()[BLINDSIGN_IX] == 1 } // The inline(never) is important. Otherwise weird segmentation faults happen on speculos. #[inline(never)] - pub fn set(&mut self, v: &u8) { + pub fn set_blind_sign(&mut self, enabled: bool) { + #[allow(static_mut_refs)] let settings = unsafe { SETTINGS.get_mut() }; - settings.update(v); + let mut switch_values: [u8; SETTINGS_SIZE] = *settings.get_ref(); + if enabled { + switch_values[BLINDSIGN_IX] = 1; + } else { + switch_values[BLINDSIGN_IX] = 0; + } + settings.update(&switch_values); } } diff --git a/rust-app/src/ui.rs b/rust-app/src/ui.rs new file mode 100644 index 00000000..c4cf13e9 --- /dev/null +++ b/rust-app/src/ui.rs @@ -0,0 +1,9 @@ +#[cfg(any(target_os = "stax", target_os = "flex"))] +pub mod nbgl; +#[cfg(any(target_os = "stax", target_os = "flex"))] +pub use nbgl::*; + +#[cfg(not(any(target_os = "stax", target_os = "flex")))] +pub mod nano; +#[cfg(not(any(target_os = "stax", target_os = "flex")))] +pub use nano::*; diff --git a/rust-app/src/ui/nano.rs b/rust-app/src/ui/nano.rs new file mode 100644 index 00000000..e34d6fd3 --- /dev/null +++ b/rust-app/src/ui/nano.rs @@ -0,0 +1,92 @@ +use crate::interface::*; +use crate::utils::*; +use core::fmt::Write; +use ledger_crypto_helpers::common::HexSlice; +use ledger_crypto_helpers::hasher::HexHash; +use ledger_prompts_ui::*; + +#[derive(Copy, Clone)] +pub struct UserInterface {} + +impl UserInterface { + pub fn confirm_address(&self, address: &SuiPubKeyAddress) -> Option<()> { + scroller("Provide Public Key", |_w| Ok(()))?; + scroller_paginated("Address", |w| Ok(write!(w, "{address}")?))?; + final_accept_prompt(&[]) + } + + pub fn confirm_sign_tx( + &self, + address: &SuiPubKeyAddress, + recipient: [u8; 32], + total_amount: u64, + gas_budget: u64, + ) -> Option<()> { + scroller("Transfer", |w| Ok(write!(w, "SUI")?))?; + + scroller_paginated("From", |w| Ok(write!(w, "{address}")?))?; + scroller_paginated("To", |w| Ok(write!(w, "0x{}", HexSlice(&recipient))?))?; + + let (quotient, remainder_str) = get_amount_in_decimals(total_amount); + scroller_paginated("Amount", |w| { + Ok(write!(w, "SUI {quotient}.{}", remainder_str.as_str())?) + })?; + + let (quotient, remainder_str) = get_amount_in_decimals(gas_budget); + scroller("Max Gas", |w| { + Ok(write!(w, "SUI {}.{}", quotient, remainder_str.as_str())?) + })?; + final_accept_prompt(&["Sign Transaction?"]) + } + + pub fn confirm_blind_sign_tx(&self, hash: &HexHash<32>) -> Option<()> { + scroller("WARNING", |w| Ok(write!(w, "Transaction not recognized")?))?; + scroller("Transaction Hash", |w| Ok(write!(w, "0x{hash}")?))?; + final_accept_prompt(&["Blind Sign Transaction?"]) + } + + pub fn warn_tx_not_recognized(&self) { + scroller("WARNING", |w| { + Ok(write!( + w, + "Transaction not recognized, enable blind signing to sign unknown transactions" + )?) + }); + } +} + +#[cfg(not(target_os = "nanos"))] +#[inline(never)] +pub fn scroller Fn(&mut PromptWrite<'b, 16>) -> Result<(), ScrollerError>>( + title: &str, + prompt_function: F, +) -> Option<()> { + ledger_prompts_ui::write_scroller_three_rows(false, title, prompt_function) +} + +#[cfg(target_os = "nanos")] +#[inline(never)] +pub fn scroller Fn(&mut PromptWrite<'b, 16>) -> Result<(), ScrollerError>>( + title: &str, + prompt_function: F, +) -> Option<()> { + ledger_prompts_ui::write_scroller(false, title, prompt_function) +} + +#[cfg(not(target_os = "nanos"))] +#[inline(never)] +pub fn scroller_paginated Fn(&mut PromptWrite<'b, 16>) -> Result<(), ScrollerError>>( + title: &str, + prompt_function: F, +) -> Option<()> { + ledger_prompts_ui::write_scroller_three_rows(true, title, prompt_function) +} + +#[cfg(target_os = "nanos")] +#[inline(never)] +pub fn scroller_paginated Fn(&mut PromptWrite<'b, 16>) -> Result<(), ScrollerError>>( + title: &str, + prompt_function: F, +) -> Option<()> { + ledger_prompts_ui::write_scroller(true, title, prompt_function) +} diff --git a/rust-app/src/ui/nbgl.rs b/rust-app/src/ui/nbgl.rs new file mode 100644 index 00000000..7de6c396 --- /dev/null +++ b/rust-app/src/ui/nbgl.rs @@ -0,0 +1,127 @@ +use crate::interface::*; +use crate::utils::*; + +extern crate alloc; +use alloc::format; + +use core::cell::RefCell; +use include_gif::include_gif; +use ledger_crypto_helpers::common::HexSlice; +use ledger_crypto_helpers::hasher::HexHash; +use ledger_device_sdk::nbgl::*; + +pub const APP_ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("sui_64x64.gif", NBGL)); + +#[derive(Copy, Clone)] +pub struct UserInterface { + pub main_menu: &'static RefCell, + pub do_refresh: &'static RefCell, +} + +impl UserInterface { + pub fn show_main_menu(&self) { + let refresh = self.do_refresh.replace(false); + if refresh { + self.main_menu.borrow_mut().show_and_return(); + } + } + + pub fn confirm_address(&self, address: &SuiPubKeyAddress) -> Option<()> { + self.do_refresh.replace(true); + let success = NbglAddressReview::new() + .glyph(&APP_ICON) + .verify_str("Provide Public Key") + .show(&format!("{address}")); + NbglReviewStatus::new() + .status_type(StatusType::Address) + .show(success); + if success { + Some(()) + } else { + None + } + } + + pub fn confirm_sign_tx( + &self, + address: &SuiPubKeyAddress, + recipient: [u8; 32], + total_amount: u64, + gas_budget: u64, + ) -> Option<()> { + self.do_refresh.replace(true); + let tx_fields = [ + Field { + name: "From", + value: &format!("{address}"), + }, + Field { + name: "To", + value: &format!("0x{}", HexSlice(&recipient)), + }, + Field { + name: "Amount", + value: { + let (quotient, remainder_str) = get_amount_in_decimals(total_amount); + &format!("SUI {}.{}", quotient, remainder_str.as_str()) + }, + }, + Field { + name: "Max Gas", + value: { + let (quotient, remainder_str) = get_amount_in_decimals(gas_budget); + &format!("SUI {}.{}", quotient, remainder_str.as_str()) + }, + }, + ]; + + let success = NbglReview::new() + .glyph(&APP_ICON) + .titles("Transfer SUI", "", "Sign Transaction?") + .show(&tx_fields); + if success { + Some(()) + } else { + None + } + } + + pub fn confirm_blind_sign_tx(&self, hash: &HexHash<32>) -> Option<()> { + self.do_refresh.replace(true); + let tx_fields = [Field { + name: "Transaction hash", + value: &format!("0x{hash}"), + }]; + + let success = NbglReview::new() + .glyph(&APP_ICON) + .blind() + .titles("Blind Sign Transaction", "", "Sign Transaction?") + .show(&tx_fields); + NbglReviewStatus::new() + .status_type(StatusType::Transaction) + .show(success); + if success { + Some(()) + } else { + None + } + } + + pub fn warn_tx_not_recognized(&self) { + let choice = NbglChoice::new().show( + "This transaction cannot be clear-signed", + "Enable blind-signing in the settings to sign this transaction", + "Go to settings", + "Reject transaction", + ); + if choice { + let mut mm = self.main_menu.borrow_mut(); + mm.set_start_page(PageIndex::Settings(0)); + mm.show_and_return(); + mm.set_start_page(PageIndex::Home); + } else { + self.do_refresh.replace(true); + } + } +} diff --git a/rust-app/src/utils.rs b/rust-app/src/utils.rs index 448349ab..b883175c 100644 --- a/rust-app/src/utils.rs +++ b/rust-app/src/utils.rs @@ -1,5 +1,3 @@ -use ledger_prompts_ui::{PromptWrite, ScrollerError}; - // A couple type ascription functions to help the compiler along. pub const fn mkfn(q: fn(&A, &mut B) -> C) -> fn(&A, &mut B) -> C { q @@ -13,42 +11,6 @@ q } */ -#[cfg(not(target_os = "nanos"))] -#[inline(never)] -pub fn scroller Fn(&mut PromptWrite<'b, 16>) -> Result<(), ScrollerError>>( - title: &str, - prompt_function: F, -) -> Option<()> { - ledger_prompts_ui::write_scroller_three_rows(false, title, prompt_function) -} - -#[cfg(target_os = "nanos")] -#[inline(never)] -pub fn scroller Fn(&mut PromptWrite<'b, 16>) -> Result<(), ScrollerError>>( - title: &str, - prompt_function: F, -) -> Option<()> { - ledger_prompts_ui::write_scroller(false, title, prompt_function) -} - -#[cfg(not(target_os = "nanos"))] -#[inline(never)] -pub fn scroller_paginated Fn(&mut PromptWrite<'b, 16>) -> Result<(), ScrollerError>>( - title: &str, - prompt_function: F, -) -> Option<()> { - ledger_prompts_ui::write_scroller_three_rows(true, title, prompt_function) -} - -#[cfg(target_os = "nanos")] -#[inline(never)] -pub fn scroller_paginated Fn(&mut PromptWrite<'b, 16>) -> Result<(), ScrollerError>>( - title: &str, - prompt_function: F, -) -> Option<()> { - ledger_prompts_ui::write_scroller(true, title, prompt_function) -} - use core::future::Future; use core::pin::*; use core::task::*; @@ -63,3 +25,28 @@ impl Future for NoinlineFut { self.project().0.poll(cx) } } + +use arrayvec::ArrayString; + +pub fn get_amount_in_decimals(amount: u64) -> (u64, ArrayString<12>) { + let factor_pow = 9; + let factor = u64::pow(10, factor_pow); + let quotient = amount / factor; + let remainder = amount % factor; + let mut remainder_str: ArrayString<12> = ArrayString::new(); + { + // Make a string for the remainder, containing at lease one zero + // So 1 SUI will be displayed as "1.0" + let mut rem = remainder; + for i in 0..factor_pow { + let f = u64::pow(10, factor_pow - i - 1); + let r = rem / f; + let _ = remainder_str.try_push(char::from(b'0' + r as u8)); + rem %= f; + if rem == 0 { + break; + } + } + } + (quotient, remainder_str) +} diff --git a/rust-app/sui_32x32.gif b/rust-app/sui_32x32.gif new file mode 100644 index 00000000..637832a1 Binary files /dev/null and b/rust-app/sui_32x32.gif differ diff --git a/rust-app/sui_40x40.gif b/rust-app/sui_40x40.gif new file mode 100644 index 00000000..0972024a Binary files /dev/null and b/rust-app/sui_40x40.gif differ diff --git a/rust-app/sui_64x64.gif b/rust-app/sui_64x64.gif new file mode 100644 index 00000000..bd6ac706 Binary files /dev/null and b/rust-app/sui_64x64.gif differ diff --git a/rust-app/tests/ts-tests.rs b/rust-app/tests/ts-tests.rs deleted file mode 100644 index 81287eeb..00000000 --- a/rust-app/tests/ts-tests.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![no_std] -#![feature(custom_test_frameworks)] -#![test_runner(crate::my_runner)] -#![no_main] - -use ledger_device_sdk::exit_app; - -use sui::main_nanos::*; - -#[no_mangle] -extern "C" fn sample_main() { - app_main() -} - -fn my_runner(_: &[&i32]) {} - -use core::panic::PanicInfo; -#[panic_handler] -fn panic_handler(_: &PanicInfo) -> ! { - exit_app(0); -} diff --git a/speculos-wrapper b/speculos-wrapper deleted file mode 100755 index 3e7d5955..00000000 --- a/speculos-wrapper +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -echo "Speculos Wrapper called with $*" - -# Specify args for mocha / 'yarn run test' like this -# cargo test --test ts-tests --target=nanos.json -- --mocha-args="--grep 'provides a public key'" -# cargo test --test ts-tests --target=nanos.json -- --mocha-args="--timeout 30000" -MOCHA_ARGS="" - -API_PORT=5005 - -run_ts_tests() { - speculos --api-port "$API_PORT" "$@" --display headless & - SPECULOS=$! - until wget -O/dev/null -o/dev/null http://localhost:$API_PORT/; do sleep 0.1; done; - cd ../ts-tests; - if ! [ -d "node_modules" ] ; then yarn install; fi - echo $MOCHA_ARGS | xargs yarn run test -- - kill $SPECULOS -} - -last="${@:$#}" - -case $last in - --mocha-args=*) - echo "Matched --mocha-args=*" - MOCHA_ARGS=${last:13} # strip --mocha-args= - echo "Passing following args to yarn run test: $MOCHA_ARGS" - # Pass all args, except the last, to the speculos - run_ts_tests "${@:1:$#-1}" - ;; - *ts_tests*|*docker-outputs*) - echo "Matched *ts_tests* / *docker-outputs*" - run_ts_tests "$@" - ;; - */deps/*) # Assume anything in the deps directory is a test, not the full app. - echo "Matched *tests*" - speculos --api-port "$API_PORT" "$@" --display headless ;; - *) speculos --api-port "$API_PORT" "$@" ;; -esac diff --git a/ts-tests/.gitignore b/ts-tests/.gitignore deleted file mode 100644 index 07e6e472..00000000 --- a/ts-tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/node_modules diff --git a/ts-tests/.mocharc.js b/ts-tests/.mocharc.js deleted file mode 100644 index 514b3b10..00000000 --- a/ts-tests/.mocharc.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - timeout: process.env.LEDGER_LIVE_HARDWARE ? 0 : 16 * 1000 -}; -console.log("Config file loaded."); -console.log(module.exports); diff --git a/ts-tests/Sui.ts b/ts-tests/Sui.ts deleted file mode 100644 index 10685217..00000000 --- a/ts-tests/Sui.ts +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************** - * Ledger Node JS API - * (c) 2016-2017 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. - ********************************************************************************/ -import type Transport from "@ledgerhq/hw-transport"; -import { Common } from "hw-app-alamgu"; -import type { SignTransactionResult, GetVersionResult } from "hw-app-alamgu"; - -export type { SignTransactionResult, GetVersionResult }; - -export type GetPublicKeyResult = { - publicKey: Uint8Array; - address: Uint8Array; -}; - -/** - * Sui API - * - * @example - * import Sui from "hw-app-sui"; - * const sui = new Sui(transport) - */ - -export default class Sui extends Common { - - constructor(transport: Transport) { - super(transport, "SUI"); - this.sendChunks = this.sendWithBlocks; - } - - /** - * Retrieves the public key associated with a particular BIP32 path from the ledger -app. - * - * @param path - the path to retrieve. - */ - override async getPublicKey( - path: string, - ): Promise { - const { publicKey, address } = await super.getPublicKey(path); - if (address == null) { - throw "should never happen, app always returns address"; - } - return { publicKey, address }; - } -} - diff --git a/ts-tests/common.ts b/ts-tests/common.ts deleted file mode 100644 index d02d49b9..00000000 --- a/ts-tests/common.ts +++ /dev/null @@ -1,201 +0,0 @@ -import SpeculosTransport from '@ledgerhq/hw-transport-node-speculos'; -import Axios from 'axios'; -import Transport from "./http-transport"; -import Sui from "./Sui"; -import { expect } from 'chai'; - -export const VERSION = { - major: 0, - minor: 2, - patch: 1, -}; - -const ignoredScreens = [ "Cancel", "Working...", "Quit", "Version" - - /* App name and version */ - , "Sui", "ui", `${VERSION.major}.${VERSION.minor}.${VERSION.patch}` - - , "Settings", "Blind Signing", "Enabled", "Disabled", "Back" - /* The next ones are specifically for S+ in which OCR is broken */ - , "ettings", "Blind igning" - ]; - -const API_PORT: number = 5005; - -const BASE_URL: string = `http://127.0.0.1:${API_PORT}`; - -const setAcceptAutomationRules = async function() { - await Axios.post(BASE_URL + "/automation", { - version: 1, - rules: [ - ... ignoredScreens.map(txt => { return { "text": txt, "actions": [] } }), - { "y": 16, "actions": [] }, - { "y": 31, "actions": [] }, - { "y": 46, "actions": [] }, - { - "text": "Confirm", - "actions": [ - [ "button", 1, true ], - [ "button", 2, true ], - [ "button", 2, false ], - [ "button", 1, false ], - ], - }, - { - "text": " Confirm", // On S+/X there is an extra space - "actions": [ - [ "button", 1, true ], - [ "button", 2, true ], - [ "button", 2, false ], - [ "button", 1, false ], - ], - }, - { - "actions": [ - [ "button", 2, true ], - [ "button", 2, false ], - ], - } - ] - }); -} - -const processPrompts = function(prompts: any[]) { - const i = prompts.filter((a : any) => !ignoredScreens.includes(a["text"])); // .values(); - let header = ""; - let prompt = ""; - let rv = []; - for (var ii in i) { - const value = i[ii]; - delete value.w; - delete value.h; - if(value["y"] == 3 || value["y"] == 4) { // S is 4, S+ is somehow 3 - if(value["text"] != header) { - if(header || prompt) rv.push({ header, prompt }); - header = value["text"]; - prompt = ""; - } - } else if(value["y"] == 16) { - prompt += value["text"]; - } else if((value["y"] == 31)) { - prompt += value["text"]; - } else if((value["y"] == 46)) { - prompt += value["text"]; - } else { - if(header || prompt) rv.push({ header, prompt }); - if(value["text"] == " Confirm") { // On S+/X there is an extra space - value["text"] = "Confirm"; - } - rv.push(value); - header = ""; - prompt = ""; - } - } - if (header || prompt) rv.push({ header, prompt }); - return rv; -} - -const fixActualPromptsForSPlus = function(prompts: any[]) { - return prompts.map ( (value) => { - if (value["text"]) { - value["x"] = ""; - value["y"] = ""; - } - return value; - }); -} - -// HACK to workaround the OCR bug https://github.com/LedgerHQ/speculos/issues/204 -const fixRefPromptsForSPlus = function(prompts: any[]) { - return prompts.map ( (value) => { - const fixF = (str: string) => { - return str.replace(/S/g,"").replace(/I/g, "l"); - }; - if (value["header"]) { - value["header"] = fixF(value["header"]); - value["prompt"] = fixF(value["prompt"]); - } else if (value["text"]) { - value["text"] = fixF(value["text"]); - value["x"] = ""; - value["y"] = ""; - } - return value; - }); -} - -const paginate_prompts = function(page_length: number, prompts: any[]) { - let rv = []; - for (var ii in prompts) { - const value = prompts[ii]; - if (value["paginate"]) { - const header = value["header"]; - const prompt = value["prompt"]; - let prompt_chunks = prompt.match(new RegExp('.{1,' + page_length + '}', 'g')); - if (prompt_chunks.length == 1) { - rv.push({header, prompt}); - } else { - for (var j in prompt_chunks) { - const chunk = prompt_chunks[j]; - let header_j = header + " (" + (Number(j) + 1).toString() + "/" + prompt_chunks.length.toString() + ")"; - rv.push({"header": header_j, "prompt": chunk}); - } - } - } else { - rv.push(value); - } - } - return rv; -} - -const sendCommandAndAccept = async function(command : any, prompts : any[]) { - await setAcceptAutomationRules(); - await Axios.delete(BASE_URL + "/events"); - - const transport = await Transport.open(BASE_URL + "/apdu"); - const client = new Sui(transport); - let err = null; - - try { await command(client); } catch(e) { - err = e; - } - if(err) throw(err); - - const actual_prompts = processPrompts((await Axios.get(BASE_URL + "/events")).data["events"] as any[]); - try { - expect(actual_prompts).to.deep.equal(paginate_prompts(16, prompts)); - } catch(e) { - try { - expect(fixActualPromptsForSPlus(actual_prompts)).to.deep.equal(fixRefPromptsForSPlus(paginate_prompts(48, prompts))); - } catch (_) { - // Throw the original error if there is a mismatch as it is generally more useful - throw(e); - } - } -} - -const sendCommandExpectFail = async function(command : any) { - await setAcceptAutomationRules(); - await Axios.delete(BASE_URL + "/events"); - - const transport = await Transport.open(BASE_URL + "/apdu"); - const client = new Sui(transport); - // client.sendChunks = client.sendWithBlocks; // Use Block protocol - - try { await command(client); } catch(e) { - return; - } - expect.fail("Command should have failed"); -} - -let toggleBlindSigningSettings = async function() { - await Axios.post(BASE_URL + "/button/right", {"action":"press-and-release"}); - await Axios.post(BASE_URL + "/button/right", {"action":"press-and-release"}); - await Axios.post(BASE_URL + "/button/both", {"action":"press-and-release"}); - await Axios.post(BASE_URL + "/button/both", {"action":"press-and-release"}); - await Axios.post(BASE_URL + "/button/right", {"action":"press-and-release"}); - await Axios.post(BASE_URL + "/button/both", {"action":"press-and-release"}); - await Axios.post(BASE_URL + "/button/left", {"action":"press-and-release"}); - await Axios.post(BASE_URL + "/button/left", {"action":"press-and-release"}); -} - -export { sendCommandAndAccept, BASE_URL, sendCommandExpectFail, toggleBlindSigningSettings } diff --git a/ts-tests/default.nix b/ts-tests/default.nix deleted file mode 100644 index e4c77c64..00000000 --- a/ts-tests/default.nix +++ /dev/null @@ -1,119 +0,0 @@ -{ pkgs ? import (import ../dep/alamgu/thunk.nix + "/dep/nixpkgs") {} -, nodejs ? pkgs.nodejs -}: - -let - inherit (pkgs) lib; - yarn2nix = import ../dep/yarn2nix { inherit pkgs; }; - inherit (import (import ../dep/alamgu/thunk.nix) {}) thunkSource; - yarnDepsNix = pkgs.runCommand "yarn-deps.nix" {} '' - ${yarn2nix}/bin/yarn2nix --offline ${./yarn.lock} > $out - ''; - yarnPackageNix = pkgs.runCommand "yarn-package.nix" {} '' - ${yarn2nix}/bin/yarn2nix --template ${./package.json} > $out - ''; - nixLib = yarn2nix.nixLib; - - localOverrides = self: super: - let - registries = { - yarn = n: v: "https://registry.yarnpkg.com/${n}/-/${n}-${v}.tgz"; - }; - y = registries.yarn; - s = self; - in { - "bcrypto@5.3.0" = super._buildNodePackage { - key="bcrypto"; - version="5.3.0"; - src = pkgs.fetchurl { - url = y "bcrypto" "5.3.0"; - sha1 = "d2d7d8a808b5efeb09fe529034a30bd772902d84"; - }; - buildPhase = '' - ${pkgs.nodePackages.node-gyp}/bin/node-gyp rebuild --nodedir=${lib.getDev nodejs} # /include/node - ''; - nativeBuildInputs = [ pkgs.python3 ]; - nodeBuildInputs = [ - (s."bufio@~1.0.7") - (s."loady@~0.0.5") - ]; - }; - - # https://github.com/Profpatsch/yarn2nix/issues/56 - "char-regex@1.0.2" = { - inherit (super."char-regex@1.0.2") key; - drv = super."char-regex@1.0.2".drv.overrideAttrs (_: { - dontMakeSourcesWritable = true; - postUnpack = '' - chmod +x $sourceRoot - chmod -R +rw $sourceRoot - ''; - }); - }; - - "usb@1.8.8" = { - inherit (super."usb@1.8.8") key; - drv = super."usb@1.8.8".drv.overrideAttrs (attrs: { - nativeBuildInputs = [ pkgs.python3 pkgs.systemd pkgs.v8 nodejs pkgs.libusb1 ]; - dontBuild = false; - buildPhase = '' - ln -s ${nixLib.linkNodeDeps { name=attrs.name; dependencies=attrs.passthru.nodeBuildInputs; }} node_modules - ${pkgs.nodePackages.node-gyp}/bin/node-gyp rebuild --nodedir=${lib.getDev nodejs} # /include/node - ''; - }); - }; - - "node-hid@1.3.0" = { - inherit (super."node-hid@1.3.0") key; - drv = super."node-hid@1.3.0".drv.overrideAttrs (attrs: { - nativeBuildInputs = [ pkgs.python3 pkgs.systemd pkgs.v8 nodejs pkgs.libusb1 pkgs.pkg-config ]; - dontBuild = false; - buildPhase = '' - ln -s ${nixLib.linkNodeDeps { name=attrs.name; dependencies=attrs.passthru.nodeBuildInputs; }} node_modules - ${pkgs.nodePackages.node-gyp}/bin/node-gyp rebuild --nodedir=${lib.getDev nodejs} # /include/node - ''; - }); - }; - - }; - - deps = nixLib.buildNodeDeps - (lib.composeExtensions - (pkgs.callPackage yarnDepsNix { - fetchgit = builtins.fetchGit; - }) - localOverrides); - - src0 = lib.sources.cleanSourceWith { - src = ./.; - filter = p: _: let - p' = baseNameOf p; - srcStr = builtins.toString ./.; - in p' != "node_modules"; - }; - - src = lib.sources.sourceFilesBySuffices src0 [ - ".js" ".cjs" ".ts" ".json" - ]; -in rec { - inherit deps yarnDepsNix yarnPackageNix thunkSource; - - testModules = nixLib.buildNodePackage ({ - src = pkgs.runCommand "package-json" {} '' - mkdir $out - cp ${./package.json} $out/package.json - ''; - } // nixLib.callTemplate yarnPackageNix deps); - - testScript = pkgs.writeShellScriptBin "mocha-wrapper" '' - set -e - cd ${testPackage} - export NODE_PATH=${testPackage}/node_modules - export NO_UPDATE_NOTIFIER=true - exec ${pkgs.yarn}/bin/yarn --offline run test - ''; - - testPackage = nixLib.buildNodePackage ({ - inherit src; - } // nixLib.callTemplate yarnPackageNix deps); -} diff --git a/ts-tests/get-version-tests.ts b/ts-tests/get-version-tests.ts deleted file mode 100644 index 9442283f..00000000 --- a/ts-tests/get-version-tests.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { VERSION, sendCommandAndAccept } from "./common"; -import { expect } from 'chai'; -import { describe, it } from 'mocha'; - -describe("get version tests", function() { - it("can get app version", async () => { - await sendCommandAndAccept(async (client : any) => { - var rv = await client.getVersion(); - expect(rv.major).to.equal(VERSION.major); - expect(rv.minor).to.equal(VERSION.minor); - expect(rv.patch).to.equal(VERSION.patch); - }, []); - }); -}); diff --git a/ts-tests/http-transport.ts b/ts-tests/http-transport.ts deleted file mode 100644 index e46b8159..00000000 --- a/ts-tests/http-transport.ts +++ /dev/null @@ -1,57 +0,0 @@ -import Transport from "@ledgerhq/hw-transport"; -import { TransportError } from "@ledgerhq/errors"; - -import axios from "axios"; -import { log } from "@ledgerhq/logs"; - -export default class HttpTransport extends Transport { - static list = (): any => Promise.resolve([]); - static listen = (_observer: any) => ({ - unsubscribe: () => {}, - }); - static async open(url: string, timeout?: number): Promise { - // await HttpTransport.check(url, timeout); - return new HttpTransport(url); - } - - url: string; - - constructor(url: string) { - super(); - this.url = url; - } - - async exchange(apdu: Buffer): Promise { - const apduHex = apdu.toString("hex"); - log("apdu", "=> " + apduHex); - const response = await axios({ - method: "POST", - url: this.url, - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - data: { - data: apduHex, - } - }); - - if (response.status !== 200) { - throw (TransportError( - "failed to communicate to server. code=" + response.status, - "HttpTransportStatus" + response.status - ) as any); - } - - const body = (await response.data) as any; - if (body.error) throw body.error; - log("apdu", "<= " + JSON.stringify(body)); - return Buffer.from(body.data, "hex"); - } - - setScrambleKey() {} - - close(): Promise { - return Promise.resolve(); - } -} diff --git a/ts-tests/package.json b/ts-tests/package.json deleted file mode 100644 index 9407c96f..00000000 --- a/ts-tests/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "sui-ledger-tests", - "version": "0.0.1", - "src": ".", - "scripts": { - "test": "mocha --no-parallel --require ts-node/register *.ts" - }, - "dependencies": { - "@ledgerhq/hw-transport": "^6.7.0", - "@ledgerhq/logs": "^6.2.0", - "@ledgerhq/errors": "^6.2.0", - "@ledgerhq/hw-transport-node-speculos": "6.7.0", - "@types/node": "^16.10.3", - "@types/node-fetch": "^3.0.3", - "axios": "^0.22.0", - "bip32-path": "^0.4.2", - "bn.js": "^5.2.0", - "create-hash": "1.2.0", - "node-fetch": "^3.0.0", - "typescript": "^4.4.3", - "hw-app-alamgu": "^0.1.1", - "js-nacl": "^1.3.2", - "blake2b": "^2.1.4", - "@types/chai": "^4.2.22", - "@types/js-nacl": "^1.3.0", - "@types/mocha": "^9.0.0", - "chai": "^4.2.0", - "chai-bytes": "^0.1.2", - "child_process": "", - "fast-check": "^2.2.0", - "mocha": "^8.1.1", - "ts-node": "^10.2.1" - } -} diff --git a/ts-tests/public-key-tests.ts b/ts-tests/public-key-tests.ts deleted file mode 100644 index 4a617544..00000000 --- a/ts-tests/public-key-tests.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { sendCommandAndAccept, BASE_URL, sendCommandExpectFail, toggleBlindSigningSettings } from "./common"; -import { expect } from 'chai'; -import { describe, it } from 'mocha'; -import Axios from 'axios'; -import type Sui from "./Sui"; - -describe('public key tests', () => { - - afterEach( async function() { - await Axios.post(BASE_URL + "/automation", {version: 1, rules: []}); - await Axios.delete(BASE_URL + "/events"); - }); - - it('provides a public key', async () => { - - await sendCommandAndAccept(async (client : Sui) => { - const rv = await client.getPublicKey("44'/784'/0'"); - expect(new Buffer(rv.publicKey).toString('hex')).to.equal("6fc6f39448ad7af0953b78b16d0f840e6fe718ba4a89384239ff20ed088da2fa"); - expect(new Buffer(rv.address).toString('hex')).to.equal("56b19e720f3bfa8caaef806afdd5dfaffd0d6ec9476323a14d1638ad734b2ba5"); - return; - }, []); - }); - - it('does address verification', async () => { - - await sendCommandAndAccept(async (client : Sui) => { - const rv = await client.verifyAddress("44'/784'/0'"); - expect(new Buffer(rv.publicKey).toString('hex')).to.equal("6fc6f39448ad7af0953b78b16d0f840e6fe718ba4a89384239ff20ed088da2fa"); - expect(new Buffer(rv.address).toString('hex')).to.equal("56b19e720f3bfa8caaef806afdd5dfaffd0d6ec9476323a14d1638ad734b2ba5"); - return; - }, [ - { - "header": "Provide Public Key", - "prompt": "", - }, - { - "header": "Address", - "prompt": "0x56b19e720f3bfa8caaef806afdd5dfaffd0d6ec9476323a14d1638ad734b2ba5", - "paginate": true, - }, - { - "text": "Confirm", - "x": 43, - "y": 11, - }, - ]); - }); -}); diff --git a/ts-tests/signing-tests.ts b/ts-tests/signing-tests.ts deleted file mode 100644 index 5a1583a2..00000000 --- a/ts-tests/signing-tests.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { VERSION, sendCommandAndAccept, BASE_URL, sendCommandExpectFail, toggleBlindSigningSettings } from "./common"; -import { expect } from 'chai'; -import { describe, it } from 'mocha'; -import Axios from 'axios'; -import type Sui from "./Sui"; -import * as blake2b from "blake2b"; -import { instantiate, Nacl } from "js-nacl"; - -let nacl : Nacl =null; - -instantiate(n => { nacl=n; }); - -function testTransaction(path: string, txn: Buffer, prompts: any[]) { - return async () => { - await sendCommandAndAccept(async (client : Sui) => { - - const { publicKey } = await client.getPublicKey(path); - - // We don't want the prompts from getPublicKey in our result - await Axios.delete(BASE_URL + "/events"); - - const sig = await client.signTransaction(path, txn); - expect(sig.signature.length).to.equal(64); - const pass = nacl.crypto_sign_verify_detached( - sig.signature, - blake2b(32).update(txn).digest(), - publicKey, - ); - expect(pass).to.equal(true); - }, prompts); - } -} - -describe("Signing tests", function() { - before( async function() { - while(!nacl) await new Promise(r => setTimeout(r, 100)); - }) - - it("can sign a transaction", - testTransaction( - "44'/784'/0'", - Buffer.from("000000000002000840420f000000000000204f2370b2a4810ad6c8e1cfd92cc8c8818fef8f59e3a80cea17871f78d850ba4b0202000101000001010200000101006fb21feead027da4873295affd6c4f3618fe176fa2fbf3e7b5ef1d9463b31e210112a6d0c44edc630d2724b1f57fea4f93308b1d22164402c65778bd99379c4733070000000000000020f2fd3c87b227f1015182fe4348ed680d7ed32bcd3269704252c03e1d0b13d30d6fb21feead027da4873295affd6c4f3618fe176fa2fbf3e7b5ef1d9463b31e2101000000000000000c0400000000000000", "hex"), - [ - { - "header": "Transfer", - "prompt": "SUI" - }, - { - "header": "From", - "prompt": "0x56b19e720f3bfa8caaef806afdd5dfaffd0d6ec9476323a14d1638ad734b2ba5", - "paginate": true - }, - { - "header": "To", - "prompt": "0x4f2370b2a4810ad6c8e1cfd92cc8c8818fef8f59e3a80cea17871f78d850ba4b", - "paginate": true - }, - { - "header": "Amount", - "prompt": "SUI 0.001" - }, - { - "header": "Max Gas", - "prompt": "SUI 0.000001036" - }, - { - "text": "Sign Transaction?", - "x": 19, - "y": 11 - }, - { - "text": "Confirm", - "x": 43, - "y": 11, - } - ] - )); - - it("can blind sign an unknown transaction", async function () { - const path = "44'/784'/0'"; - const txn = Buffer.from("00000000050205546e7f126d2f40331a543b9608439b582fd0d103000000000000002080fdabcc90498e7eb8413b140c4334871eeafa5a86203fd9cfdb032f604f49e1284af431cf032b5d85324135bf9a3073e920d7f5020000000000000020a06f410c175e828c24cee84cb3bd95cff25c33fbbdcb62c6596e8e423784ffe702d08074075c7097f361e8b443e2075a852a2292e8a08074075c7097f361e8b443e2075a852a2292e80180969800000000001643fb2578ff7191c643079a62c1cca8ec2752bc05546e7f126d2f40331a543b9608439b582fd0d103000000000000002080fdabcc90498e7eb8413b140c4334871eeafa5a86203fd9cfdb032f604f49e101000000000000002c01000000000000", "hex"); - const prompts = - [ - { - "header": "WARNING", - "prompt": "Transaction not recognized" - }, - { - "header": "Transaction Hash", - "prompt": "0xfc2bce70e1cb980a6d49a32ff770a782ee13dabdecee085b82e0fdad5e92fcdd" - }, - { - "text": "Blind Sign Transaction?", - "x": 4, - "y": 11 - }, - { - "text": "Confirm", - "x": 43, - "y": 11, - } - ]; - - await toggleBlindSigningSettings(); - await Axios.delete(BASE_URL + "/events"); - await testTransaction(path, txn, prompts)(); - await Axios.delete(BASE_URL + "/events"); - // reset back to disabled - await toggleBlindSigningSettings(); - }); - - it("should reject signing a non-SUI coin transaction, if blind signing is not enabled", async function () { - const path = "44'/784'/0'"; - const txn = Buffer.from("AAAAAAADAQAe2uv1Mds+xCVK5Jv/Dv5cgEl/9DthDcpbjWcsmFpzbs6BNQAAAAAAIKPD8GQqgBpJZRV+nFDRE7rqR0Za8x0pyfLusVdpPPVRAAgADl+jHAAAAAAg5y3MHATlk+Ik5cPIdEz5iPANs1jcXZHVGjh4Mb16lwkCAgEAAAEBAQABAQIAAAECAF/sd27xyQe/W+gY4WRtPlQro1siWQu79s0pxbbCSRafAfnjaU5yJSFFDJznsAaBqbkiR9CB8DJqWki8fn8AUZeQz4E1AAAAAAAgTRU/MsawTJirpVwjDF8gyiEbaT0+7J0V8ifUEGGBkcVf7Hdu8ckHv1voGOFkbT5UK6NbIlkLu/bNKcW2wkkWn+gDAAAAAAAA8NdGAAAAAAAA", "base64"); - - await sendCommandExpectFail(async (client : Sui) => { - await client.signTransaction(path, txn); - }); - }); - - it("should reject signing an unknown transaction, if blind signing is not enabled", async function () { - const path = "44'/784'/0'"; - const txn = Buffer.from("00000000050205546e7f126d2f40331a543b9608439b582fd0d103000000000000002080fdabcc90498e7eb8413b140c4334871eeafa5a86203fd9cfdb032f604f49e1284af431cf032b5d85324135bf9a3073e920d7f5020000000000000020a06f410c175e828c24cee84cb3bd95cff25c33fbbdcb62c6596e8e423784ffe702d08074075c7097f361e8b443e2075a852a2292e8a08074075c7097f361e8b443e2075a852a2292e80180969800000000001643fb2578ff7191c643079a62c1cca8ec2752bc05546e7f126d2f40331a543b9608439b582fd0d103000000000000002080fdabcc90498e7eb8413b140c4334871eeafa5a86203fd9cfdb032f604f49e101000000000000002c01000000000000", "hex"); - - await sendCommandExpectFail(async (client : Sui) => { - await client.signTransaction(path, txn); - }); - }); - - it("Rejects a blind sign with mismatching lengths", async function () { - const path = "44'/784'/0'"; - const txn = Buffer.from("00000000050205546e7f126d2f40331a543b9608439b582fd0d103000000000000002080fdabcc90498e7eb8413b140c4334871eeafa5a86203fd9cfdb032f604f49e1284af431cf032b5d85324135bf9a3073e920d7f5020000000000000020a06f410c175e828c24cee84cb3bd95cff25c33fbbdcb62c6596e8e423784ffe702d08074075c7097f361e8b443e2075a852a2292e8a08074075c7097f361e8b443e2075a852a2292e80180969800000000001643fb2578ff7191c643079a62c1cca8ec2752bc05546e7f126d2f40331a543b9608439b582fd0d103000000000000002080fdabcc90498e7eb8413b140c4334871eeafa5a86203fd9cfdb032f604f49e101000000000000002c01000000000000", "hex"); - - await toggleBlindSigningSettings(); - await Axios.delete(BASE_URL + "/events"); - await sendCommandExpectFail(async (client : any) => { - client.oldSendChunks = client.sendChunks; - client.sendChunks = (cla, ins, p1, p2, payload) => { - payload[0][3]=payload[0][3]+20; // Add 20*2^24 to the transaction length, so we'll run out of input. - const rv = client.oldSendChunks(cla, ins, p1, p2, payload); - return rv; - } - await client.signTransaction(path, txn); - }); - // Check that the app is still running and has not crashed. - await sendCommandAndAccept( - async client => { - const { publicKey } = await client.getPublicKey(path); - expect(publicKey.length>0).to.equal(true); - }, - []); - await Axios.delete(BASE_URL + "/events"); - // reset back to disabled - await toggleBlindSigningSettings(); - }); -}); diff --git a/ts-tests/tsconfig.json b/ts-tests/tsconfig.json deleted file mode 100644 index 4f688366..00000000 --- a/ts-tests/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ts-node": { - "files": true - } -} diff --git a/ts-tests/yarn.lock b/ts-tests/yarn.lock deleted file mode 100644 index b83013bc..00000000 --- a/ts-tests/yarn.lock +++ /dev/null @@ -1,1094 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@cspotcode/source-map-support@^0.8.0": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" - integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== - dependencies: - "@jridgewell/trace-mapping" "0.3.9" - -"@jridgewell/resolve-uri@^3.0.3": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" - integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== - -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.13" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" - integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== - -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@ledgerhq/devices@^6.27.1": - version "6.27.1" - resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-6.27.1.tgz#3b13ab1d1ba8201e9e74a08f390560483978c962" - integrity sha512-jX++oy89jtv7Dp2X6gwt3MMkoajel80JFWcdc0HCouwDsV1mVJ3SQdwl/bQU0zd8HI6KebvUP95QTwbQLLK/RQ== - dependencies: - "@ledgerhq/errors" "^6.10.0" - "@ledgerhq/logs" "^6.10.0" - rxjs "6" - semver "^7.3.5" - -"@ledgerhq/errors@^6.10.0", "@ledgerhq/errors@^6.2.0": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.10.0.tgz#dda9127b65f653fbb2f74a55e8f0e550d69de6e4" - integrity sha512-fQFnl2VIXh9Yd41lGjReCeK+Q2hwxQJvLZfqHnKqWapTz68NHOv5QcI0OHuZVNEbv0xhgdLhi5b65kgYeQSUVg== - -"@ledgerhq/hw-transport-node-speculos@6.7.0": - version "6.7.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-speculos/-/hw-transport-node-speculos-6.7.0.tgz#bcda216fdbf70cd408f533a13c66287b024f8f1e" - integrity sha512-RgFom5N6XLQ3xSl+pFWZmmxQxsBdAVJTpyPoClYNoH/ETif2gQHRE1AhtYpSHeJRRMUsioZEMSqW3l8qqdjclA== - dependencies: - "@ledgerhq/errors" "^6.2.0" - "@ledgerhq/hw-transport" "^6.7.0" - "@ledgerhq/logs" "^6.2.0" - rxjs "6" - -"@ledgerhq/hw-transport@^6.3.0", "@ledgerhq/hw-transport@^6.7.0": - version "6.27.1" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.27.1.tgz#88072278f69c279cb6569352acd4ae2fec33ace3" - integrity sha512-hnE4/Fq1YzQI4PA1W0H8tCkI99R3UWDb3pJeZd6/Xs4Qw/q1uiQO+vNLC6KIPPhK0IajUfuI/P2jk0qWcMsuAQ== - dependencies: - "@ledgerhq/devices" "^6.27.1" - "@ledgerhq/errors" "^6.10.0" - events "^3.3.0" - -"@ledgerhq/logs@^6.10.0", "@ledgerhq/logs@^6.2.0": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.10.0.tgz#c012c1ecc1a0e53d50e6af381618dca5268461c1" - integrity sha512-lLseUPEhSFUXYTKj6q7s2O3s2vW2ebgA11vMAlKodXGf5AFw4zUoEbTz9CoFOC9jS6xY4Qr8BmRnxP/odT4Uuw== - -"@tsconfig/node10@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" - integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== - -"@tsconfig/node12@^1.0.7": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" - integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== - -"@tsconfig/node14@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" - integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== - -"@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== - -"@types/chai@^4.2.22": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.1.tgz#e2c6e73e0bdeb2521d00756d099218e9f5d90a04" - integrity sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ== - -"@types/js-nacl@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@types/js-nacl/-/js-nacl-1.3.0.tgz#85d81ffbb486bb584286fd62a8f87f5e3177861b" - integrity sha512-juUvxo444ZfwDSwWyhssMxSN+snqTdiUoOVXZF+/ffVrGHq3rAf1fmczWn3z9TCEAuRbaTmgAcYlZ9MutyyOkQ== - -"@types/mocha@^9.0.0": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" - integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== - -"@types/node-fetch@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-3.0.3.tgz#9d969c9a748e841554a40ee435d26e53fa3ee899" - integrity sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g== - dependencies: - node-fetch "*" - -"@types/node@^16.10.3": - version "16.11.41" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.41.tgz#88eb485b1bfdb4c224d878b7832239536aa2f813" - integrity sha512-mqoYK2TnVjdkGk8qXAVGc/x9nSaTpSrFaGFm43BUH3IdoBV0nta6hYaGmdOvIMlbHJbUEVen3gvwpwovAZKNdQ== - -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== - -acorn-walk@^8.1.1: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== - -acorn@^8.4.1: - version "8.7.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== - -ansi-colors@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-regex@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" - integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -anymatch@~3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" - integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== - -axios@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.22.0.tgz#bf702c41fb50fbca4539589d839a077117b79b25" - integrity sha512-Z0U3uhqQeg1oNcihswf4ZD57O3NrR1+ZXhxaROaWpDmsDTx7T2HNBV2ulBtie2hwJptu8UvgnJoK+BIqdzh/1w== - dependencies: - follow-redirects "^1.14.4" - -b4a@^1.0.1: - version "1.5.3" - resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.5.3.tgz#56293b5607aeda3fd81c481e516e9f103fc88341" - integrity sha512-1aCQIzQJK7G0z1Una75tWMlwVAR8o+QHoAlnWc5XAxRVBESY9WsitfBgM5nPyDBP5HrhPU1Np4Pq2Y7CJQ+tVw== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -bip32-path@^0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/bip32-path/-/bip32-path-0.4.2.tgz#5db0416ad6822712f077836e2557b8697c0c7c99" - integrity sha512-ZBMCELjJfcNMkz5bDuJ1WrYvjlhEF5k6mQ8vUr4N7MbVRsXei7ZOg8VhhwMfNiW68NWmLkgkc6WvTickrLGprQ== - -blake2b-wasm@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz#9115649111edbbd87eb24ce7c04b427e4e2be5be" - integrity sha512-S1kwmW2ZhZFFFOghcx73+ZajEfKBqhP82JMssxtLVMxlaPea1p9uoLiUZ5WYyHn0KddwbLc+0vh4wR0KBNoT5w== - dependencies: - b4a "^1.0.1" - nanoassert "^2.0.0" - -blake2b@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/blake2b/-/blake2b-2.1.4.tgz#817d278526ddb4cd673bfb1af16d1ad61e393ba3" - integrity sha512-AyBuuJNI64gIvwx13qiICz6H6hpmjvYS5DGkG6jbXMOT8Z3WUJ3V1X0FlhIoT1b/5JtHE3ki+xjtMvu1nn+t9A== - dependencies: - blake2b-wasm "^2.4.0" - nanoassert "^2.0.0" - -bn.js@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -camelcase@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -chai-bytes@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/chai-bytes/-/chai-bytes-0.1.2.tgz#c297e81d47eb3106af0676ded5bb5e0c9f981db3" - integrity sha512-0ol6oJS0y1ozj6AZK8n1pyv1/G+l44nqUJygAkK1UrYl+IOGie5vcrEdrAlwmLYGIA9NVvtHWosPYwWWIXf/XA== - -chai@^4.2.0: - version "4.3.6" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c" - integrity sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q== - dependencies: - assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^3.0.1" - get-func-name "^2.0.0" - loupe "^2.3.1" - pathval "^1.1.1" - type-detect "^4.0.5" - -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== - -child_process@: - version "1.0.2" - resolved "https://registry.yarnpkg.com/child_process/-/child_process-1.0.2.tgz#b1f7e7fc73d25e7fd1d455adc94e143830182b5a" - integrity sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g== - -chokidar@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.5.0" - optionalDependencies: - fsevents "~2.3.1" - -cipher-base@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -create-hash@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-require@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - -data-uri-to-buffer@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz#b5db46aea50f6176428ac05b73be39a57701a64b" - integrity sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA== - -debug@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== - dependencies: - type-detect "^4.0.0" - -diff@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -events@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -fast-check@^2.2.0: - version "2.25.0" - resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-2.25.0.tgz#5146601851bf3be0953bd17eb2b7d547936c6561" - integrity sha512-wRUT2KD2lAmT75WNIJIHECawoUUMHM0I5jrlLXGtGeqmPL8jl/EldUDjY1VCp6fDY8yflyfUeIOsOBrIbIiArg== - dependencies: - pure-rand "^5.0.1" - -fast-sha256@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/fast-sha256/-/fast-sha256-1.3.0.tgz#7916ba2054eeb255982608cccd0f6660c79b7ae6" - integrity sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ== - -fetch-blob@^3.1.2, fetch-blob@^3.1.4: - version "3.1.5" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.1.5.tgz#0077bf5f3fcdbd9d75a0b5362f77dbb743489863" - integrity sha512-N64ZpKqoLejlrwkIAnb9iLSA3Vx/kjgzpcDhygcqJ2KKjky8nCgUQ+dzXtbrLaWZGZNmNfQTsiQ0weZ1svglHg== - dependencies: - node-domexception "^1.0.0" - web-streams-polyfill "^3.0.3" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -find-up@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -follow-redirects@^1.14.4: - version "1.15.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" - integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== - -formdata-polyfill@^4.0.10: - version "4.0.10" - resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" - integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== - dependencies: - fetch-blob "^3.1.2" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== - -glob-parent@~5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^8.0.3: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hw-app-alamgu@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/hw-app-alamgu/-/hw-app-alamgu-0.1.1.tgz#32213353fee56aa0d4b093d499d232977f6d354f" - integrity sha512-ONiommqXKQWrfe6PwRrUH/NckV5A2tIRGC7oLm0C7QXOm7nqY/yjTRmtJ6yB1AnHwaiIw5ts/VgSLvmNHr6J0g== - dependencies: - "@ledgerhq/hw-transport" "^6.3.0" - fast-sha256 "^1.3.0" - typedoc "^0.22.7" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -js-nacl@^1.3.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/js-nacl/-/js-nacl-1.4.0.tgz#c74663d5131b413120cfc75e509bfcdad52a9137" - integrity sha512-HgYLcutGbMYBJrwgVICiHliuw1OJLy2U3tIuK6a1rZ06KC84TPl81WG1hcBRrBCiIIuBe3PSo9G4IZOMGdSg3Q== - -js-yaml@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" - integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== - dependencies: - argparse "^2.0.1" - -jsonc-parser@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" - integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -log-symbols@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== - dependencies: - chalk "^4.0.0" - -loupe@^2.3.1: - version "2.3.4" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" - integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== - dependencies: - get-func-name "^2.0.0" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -lunr@^2.3.9: - version "2.3.9" - resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" - integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== - -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -marked@^4.0.16: - version "4.3.0" - resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" - integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== - -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -minimatch@3.0.4, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^5.0.1, minimatch@^5.1.0: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -mocha@^8.1.1: - version "8.4.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.4.0.tgz#677be88bf15980a3cae03a73e10a0fc3997f0cff" - integrity sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ== - dependencies: - "@ungap/promise-all-settled" "1.1.2" - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.1" - debug "4.3.1" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "7.1.6" - growl "1.10.5" - he "1.2.0" - js-yaml "4.0.0" - log-symbols "4.0.0" - minimatch "3.0.4" - ms "2.1.3" - nanoid "3.1.20" - serialize-javascript "5.0.1" - strip-json-comments "3.1.1" - supports-color "8.1.1" - which "2.0.2" - wide-align "1.1.3" - workerpool "6.1.0" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nanoassert@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-2.0.0.tgz#a05f86de6c7a51618038a620f88878ed1e490c09" - integrity sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA== - -nanoid@3.1.20: - version "3.1.20" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" - integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== - -node-domexception@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - -node-fetch@*, node-fetch@^3.0.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.6.tgz#6d4627181697a9d9674aae0d61548e0d629b31b9" - integrity sha512-LAy/HZnLADOVkVPubaxHDft29booGglPFDr2Hw0J1AercRh01UiVFm++KMDnJeH9sHgNB4hsXPii7Sgym/sTbw== - dependencies: - data-uri-to-buffer "^4.0.0" - fetch-blob "^3.1.4" - formdata-polyfill "^4.0.10" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -pathval@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" - integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pure-rand@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.1.tgz#97a287b4b4960b2a3448c0932bf28f2405cac51d" - integrity sha512-ksWccjmXOHU2gJBnH0cK1lSYdvSZ0zLoCMSz/nTGh6hDvCSgcRxDyIcOBD6KNxFz3xhMPm/T267Tbe2JRymKEQ== - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== - dependencies: - picomatch "^2.2.1" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -rxjs@6: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -semver@^7.3.5: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== - dependencies: - lru-cache "^6.0.0" - -serialize-javascript@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== - dependencies: - randombytes "^2.1.0" - -sha.js@^2.4.0: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shiki@^0.10.1: - version "0.10.1" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.10.1.tgz#6f9a16205a823b56c072d0f1a0bcd0f2646bef14" - integrity sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng== - dependencies: - jsonc-parser "^3.0.0" - vscode-oniguruma "^1.6.1" - vscode-textmate "5.2.0" - -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-json-comments@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -supports-color@8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -ts-node@^10.2.1: - version "10.8.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.8.1.tgz#ea2bd3459011b52699d7e88daa55a45a1af4f066" - integrity sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g== - dependencies: - "@cspotcode/source-map-support" "^0.8.0" - "@tsconfig/node10" "^1.0.7" - "@tsconfig/node12" "^1.0.7" - "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.1" - yn "3.1.1" - -tslib@^1.9.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -type-detect@^4.0.0, type-detect@^4.0.5: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -typedoc@^0.22.7: - version "0.22.18" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.22.18.tgz#1d000c33b66b88fd8cdfea14a26113a83b7e6591" - integrity sha512-NK9RlLhRUGMvc6Rw5USEYgT4DVAUFk7IF7Q6MYfpJ88KnTZP7EneEa4RcP+tX1auAcz7QT1Iy0bUSZBYYHdoyA== - dependencies: - glob "^8.0.3" - lunr "^2.3.9" - marked "^4.0.16" - minimatch "^5.1.0" - shiki "^0.10.1" - -typescript@^4.4.3: - version "4.7.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" - integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== - -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -v8-compile-cache-lib@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" - integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== - -vscode-oniguruma@^1.6.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" - integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== - -vscode-textmate@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" - integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== - -web-streams-polyfill@^3.0.3: - version "3.2.1" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" - integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== - -which@2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wide-align@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - -workerpool@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" - integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yargs-parser@20.2.4, yargs-parser@^20.2.2: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== - -yargs-unparser@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==