diff --git a/.github/workflows/build-tenv-regtest.yml b/.github/workflows/build-tenv-regtest.yml index 6c4587d..44f6411 100644 --- a/.github/workflows/build-tenv-regtest.yml +++ b/.github/workflows/build-tenv-regtest.yml @@ -13,25 +13,20 @@ jobs: runs-on: ubuntu-latest if: ${{ github.repository_owner == 'trezor' }} steps: - - - name: Checkout - uses: actions/checkout@v3 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + - name: Checkout code + uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ secrets.TREZOR_BOT_USERNAME }} password: ${{ secrets.TREZOR_BOT_TOKEN }} - - - name: Build and push bitcoin-regtest - uses: docker/build-push-action@v3 + - name: Build and push bitcoin-regtest + uses: docker/build-push-action@v6 with: context: . file: ./docker/bitcoin-regtest/Dockerfile diff --git a/.github/workflows/build-tenv-test.yml b/.github/workflows/build-tenv-test.yml index 02e23cb..ea2cd4c 100644 --- a/.github/workflows/build-tenv-test.yml +++ b/.github/workflows/build-tenv-test.yml @@ -8,23 +8,23 @@ jobs: runs-on: ubuntu-latest if: ${{ github.repository_owner == 'trezor' }} steps: - - name: Checkout - uses: actions/checkout@v2 + - name: Checkout code + uses: actions/checkout@v4 - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to GitHub Container Registry - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ secrets.TREZOR_BOT_USERNAME }} password: ${{ secrets.TREZOR_BOT_TOKEN }} - name: Build and push trezor-user-env - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v6 with: context: . - file: ./docker/Dockerfile + file: ./docker/Dockerfile-debian platforms: linux/amd64,linux/arm64 push: true tags: | diff --git a/.github/workflows/build-tenv.yml b/.github/workflows/build-tenv.yml index 720f3b3..c71ba06 100644 --- a/.github/workflows/build-tenv.yml +++ b/.github/workflows/build-tenv.yml @@ -11,28 +11,23 @@ jobs: runs-on: ubuntu-latest if: ${{ github.repository_owner == 'trezor' }} steps: - - - name: Checkout - uses: actions/checkout@v3 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + - name: Checkout code + uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ secrets.TREZOR_BOT_USERNAME }} password: ${{ secrets.TREZOR_BOT_TOKEN }} - - - name: Build and push bitcoin-regtest - uses: docker/build-push-action@v3 + - name: Build and push bitcoin-regtest + uses: docker/build-push-action@v6 with: context: . - file: ./docker/Dockerfile + file: ./docker/Dockerfile-debian platforms: linux/amd64,linux/arm64 push: true tags: | diff --git a/.github/workflows/check-validation.yml b/.github/workflows/check-validation.yml index 6543616..ca08acf 100644 --- a/.github/workflows/check-validation.yml +++ b/.github/workflows/check-validation.yml @@ -3,13 +3,12 @@ name: "[Check] validate trezor-user-env" on: [pull_request] jobs: - # Run code validation validation-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Nix - uses: cachix/install-nix-action@v18 + uses: cachix/install-nix-action@v27 with: nix_path: nixpkgs=channel:nixos-unstable diff --git a/.gitignore b/.gitignore index 22b0997..817a20f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ __pycache__ src/binaries/firmware/bin/* !src/binaries/firmware/bin/download.sh !src/binaries/firmware/bin/download_latest_gh.py +!src/binaries/firmware/bin/patch-bin.sh !src/binaries/firmware/bin/arm !src/binaries/trezord-go/bin/download.sh emulator.img diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 8aa187e..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,65 +0,0 @@ -# install the latest Alpine linux from scratch - -FROM scratch -ARG ALPINE_VERSION=3.15.0 -ARG ALPINE_ARCH=x86_64 -ADD docker/alpine-minirootfs-${ALPINE_VERSION}-${ALPINE_ARCH}.tar.gz / -# http://dl-cdn.alpinelinux.org/alpine/edge/releases/x86_64/alpine-minirootfs-3.15.0-x86_64.tar.gz - -# the following is adapted from https://github.com/NixOS/docker/blob/master/Dockerfile - -# Enable HTTPS support in wget and set nsswitch.conf to make resolution work within containers -RUN apk add --no-cache --update openssl \ - && echo hosts: files dns > /etc/nsswitch.conf -# Add basic packages -RUN apk update && apk add bash git python3 - -# Download Nix and install it into the system. -# https://nixos.org/releases/nix/nix-2.4/nix-2.4-x86_64-linux.tar.xz -ARG NIX_VERSION=2.4 -RUN wget https://nixos.org/releases/nix/nix-${NIX_VERSION}/nix-${NIX_VERSION}-${ALPINE_ARCH}-linux.tar.xz \ - && tar xf nix-${NIX_VERSION}-${ALPINE_ARCH}-linux.tar.xz \ - && addgroup -g 30000 -S nixbld \ - && for i in $(seq 1 30); do adduser -S -D -h /var/empty -g "Nix build user $i" -u $((30000 + i)) -G nixbld nixbld$i ; done \ - && mkdir -m 0755 /etc/nix \ - && echo 'sandbox = false' > /etc/nix/nix.conf \ - && mkdir -m 0755 /nix && USER=root sh nix-${NIX_VERSION}-${ALPINE_ARCH}-linux/install \ - && ln -s /nix/var/nix/profiles/default/etc/profile.d/nix.sh /etc/profile.d/ \ - && rm -r /nix-${NIX_VERSION}-${ALPINE_ARCH}-linux* \ - && rm -rf /var/cache/apk/* \ - && /nix/var/nix/profiles/default/bin/nix-collect-garbage --delete-old \ - && /nix/var/nix/profiles/default/bin/nix-store --optimise \ - && /nix/var/nix/profiles/default/bin/nix-store --verify --check-contents - -ENV \ - USER=root \ - PATH=/nix/var/nix/profiles/default/bin:/nix/var/nix/profiles/default/sbin:/bin:/sbin:/usr/bin:/usr/sbin \ - GIT_SSL_CAINFO=/etc/ssl/certs/ca-certificates.crt \ - NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \ - NIX_PATH=/nix/var/nix/profiles/per-user/root/channels - -# #################################### -# Trezor specific stuff starts here -# #################################### - -# good colors for most applications -ENV TERM xterm - -# trezor emu -ENV XDG_RUNTIME_DIR "/var/tmp" - -# trezorctl https://click.palletsprojects.com/en/7.x/python3/ -ENV LC_ALL C.UTF-8 -ENV LANG C.UTF-8 - -# copy python websocket server and all binaries -COPY ./ ./trezor-user-env - -WORKDIR ./trezor-user-env - -RUN nix-shell --run "poetry install --without dev --no-root && rm -rf $POETRY_CACHE_DIR" \ - && nix-shell --run "./src/binaries/firmware/bin/download.sh" \ - && nix-shell --run "./patch_emulators.sh src/binaries/firmware/bin" \ - && nix-shell --run "./src/binaries/trezord-go/bin/download.sh" - -CMD nix-shell --run "./run-nix.sh" diff --git a/docker/Dockerfile-debian b/docker/Dockerfile-debian new file mode 100644 index 0000000..dd1cdde --- /dev/null +++ b/docker/Dockerfile-debian @@ -0,0 +1,54 @@ +# Start with a base Debian image +FROM debian:bookworm-slim + +# Set environment variables +ENV TERM=xterm \ + XDG_RUNTIME_DIR="/var/tmp" \ + LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 \ + GIT_SSL_CAINFO=/etc/ssl/certs/ca-certificates.crt \ + NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt + +# Install required packages +RUN curl -sL https://deb.nodesource.com/setup_22.x | bash - +RUN apt-get update && apt-get install -y --no-install-recommends \ + openssl \ + bash \ + git \ + nodejs \ + python3 \ + python3-dev \ + python3-pip \ + python3-venv \ + python3-poetry \ + libsdl2-2.0-0 \ + libsdl2-image-2.0-0 \ + x11-xserver-utils \ + wget \ + unzip \ + curl \ + procps \ + build-essential \ + g++ \ + libc6 \ + libc6-dev \ + patchelf \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + + +# Add basic trezor-user-env files +COPY ./ /trezor-user-env +WORKDIR /trezor-user-env + +# Install Python dependencies using poetry +RUN poetry cache clear --all pypi +RUN poetry install --no-dev --no-root + +# Execute scripts and clean up +RUN ./src/binaries/firmware/bin/download.sh +RUN ./src/binaries/trezord-go/bin/download.sh +# Patch emulator binaries +RUN cd src/binaries/firmware/bin && ./patch-bin.sh + +# Command to run on container start +CMD poetry run python src/main.py diff --git a/docker/alpine-minirootfs-3.15.0-x86_64.tar.gz b/docker/alpine-minirootfs-3.15.0-x86_64.tar.gz deleted file mode 100644 index e9956eb..0000000 Binary files a/docker/alpine-minirootfs-3.15.0-x86_64.tar.gz and /dev/null differ diff --git a/docker/compose.yml b/docker/compose.yml index 74e79e6..e5fcc86 100644 --- a/docker/compose.yml +++ b/docker/compose.yml @@ -1,4 +1,3 @@ -version: '3' services: trezor-user-env-unix: container_name: trezor-user-env.unix diff --git a/docs/controller.md b/docs/controller.md index f539b07..fb1754e 100644 --- a/docs/controller.md +++ b/docs/controller.md @@ -24,10 +24,11 @@ - **response**: `{"bridge_status": bool, "emulator_status": bool}` - **emulator-start** - - **action**: start the specified version of emulator (and if one already runs, kills it) + - **action**: start the specified emulator (identified by model + version) (and if one already runs, kills it) - **arguments**: - - **version**: `str` (1.9.4, 2.4.0., etc.) - default is the latest TT (`2-main`) - - `1-latest` and `2-latest` can be used to get the latest released version of that model + - **model**: `str` (enum of new model names - `["T1B1", "T2T1", "T2B1", "T3T1"]`) - needs to be supplied + - **version**: `str` (1.9.4, 2.4.0., etc.) - default is the latest version from master/main branch (`-main`) + - `-latest` can be used to get the latest released version of that model (by release tag, e.g. 2.9.0) - **wipe**: `bool` (default=False) whether to delete the emulator profile before starting it - **output_to_logfile**: `bool` (default=True) whether the debug output should go to a logfile - otherwise it goes to stdout - **save_screenshots**: `bool` (default=False) whether to save screenshots to enable calling **emulator-get-screenshot** @@ -36,7 +37,7 @@ - **action**: downloads emulator from specified URL and runs it - **arguments**: - **url**: `str` from where to download the emulator - - **model**: `str` which emulator it is - either "1" for T1 or "2" for T2 + - **model**: `str` which emulator it is - `["T1B1", "T2T1", "T2B1", "T3T1"]` - **wipe**: `bool` (default=False) whether to delete the emulator profile before starting it - **output_to_logfile**: `bool` (default=True) whether the debug output should go to a logfile - otherwise it goes to stdout - **save_screenshots**: `bool` (default=False) whether to save screenshots to enable calling **emulator-get-screenshot** diff --git a/patch_emulators.sh b/patch_emulators.sh deleted file mode 100755 index a29593f..0000000 --- a/patch_emulators.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -FILE_DIR="$(dirname "${0}")" -cd ${FILE_DIR} - -DIR_TO_PATCH="${1:-src/binaries/firmware/bin}" - -echo "Patching ${DIR_TO_PATCH}" - -nix-shell --run "autoPatchelf ${DIR_TO_PATCH}" diff --git a/poetry.lock b/poetry.lock index ac63c60..e33045e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "atomicwrites" @@ -821,4 +821,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "b9a33c4905cd19f3b1748b3a33d3e42e715edf3feee7730d748e26edf21226ba" +content-hash = "9e1a10f71d58f97d2ed08be0e522d868a39767e2ae4a5420be2aeb27d7e2b04d" diff --git a/pyproject.toml b/pyproject.toml index 4a9cb1e..a0285f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ authors = ["SatoshiLabs "] [tool.poetry.dependencies] python = "^3.11" termcolor = "^1.1.0" -trezor = { git = "https://github.com/trezor/trezor-firmware.git", branch = "main", subdirectory = "python" } +trezor = "^0.13.9" websockets = "^10.1" psutil = "^5.8.0" Pillow = "^9.1.0" diff --git a/shell.nix b/shell.nix index c3dd1b2..385b1c0 100644 --- a/shell.nix +++ b/shell.nix @@ -21,7 +21,6 @@ stdenv.mkDerivation { xorg.xhost wget git - unzip curl procps ]; diff --git a/src/binaries.py b/src/binaries.py index 6c8a957..142712f 100644 --- a/src/binaries.py +++ b/src/binaries.py @@ -11,31 +11,36 @@ USER_DOWNLOADED_DIR = FIRMWARE_BIN_DIR / "user_downloaded" USER_DOWNLOADED_DIR.mkdir(exist_ok=True) -Model = Literal["1", "2", "R", "T3T1"] +Model = Literal["T1B1", "T2T1", "T2B1", "T3T1"] FIRMWARES: Dict[Model, Dict[str, str]] = { - "1": OrderedDict(), - "2": OrderedDict(), - "R": OrderedDict(), + "T1B1": OrderedDict(), + "T2T1": OrderedDict(), + "T2B1": OrderedDict(), "T3T1": OrderedDict(), } +MODEL_IDENTIFIERS: Dict[Model, str] = { + "T1B1": "trezor-emu-legacy-T1B1-v", + "T2T1": "trezor-emu-core-T2T1-v", + "T2B1": "trezor-emu-core-T2B1-v", + "T3T1": "trezor-emu-core-T3T1-v", +} + BRIDGES: List[str] = [] IS_ARM = os.uname().machine.startswith(("aarch64", "arm")) ARM_IDENTIFIER = "-arm" -IDENTIFIER_T1 = "trezor-emu-legacy-v" -IDENTIFIER_TT = "trezor-emu-core-v" -IDENTIFIER_TR = "trezor-emu-core-R-v" -IDENTIFIER_T3T1 = "trezor-emu-core-T3T1-v" - def register_new_firmware(model: Model, version: str, location: str) -> None: FIRMWARES[model][version] = location def get_firmware_location(model: Model, version: str) -> str: - return FIRMWARES[model][version] + try: + return FIRMWARES[model][version] + except KeyError: + raise RuntimeError(f"Unknown firmware {model} {version}") def get_all_firmware_versions() -> dict[Model, list[str]]: @@ -54,7 +59,9 @@ def get_main_version(model: Model) -> str: def check_model(model: str) -> None: supported_models = FIRMWARES.keys() if model not in supported_models: - raise RuntimeError(f"Unknown model {model} - supported are {supported_models}") + raise RuntimeError( + f"Unknown model {model} - supported are {list(supported_models)}" + ) def _print_in_verbose(text: str, args: Any) -> None: @@ -83,14 +90,7 @@ def explore_firmwares(args: Any) -> None: _print_in_verbose(f"On x86, ignoring ARM emulator - {fw}", args) continue - models: List[Tuple[str, Model]] = [ - (IDENTIFIER_T1, "1"), - (IDENTIFIER_TT, "2"), - (IDENTIFIER_TR, "R"), - (IDENTIFIER_T3T1, "T3T1"), - ] - - for identifier, model in models: + for model, identifier in MODEL_IDENTIFIERS.items(): if identifier in fw: version = fw.split(identifier)[-1] break @@ -140,15 +140,9 @@ def explore_bridges() -> None: if IS_ARM: BRIDGES.append(f"2.0.33{ARM_IDENTIFIER}") BRIDGES.append(f"2.0.32{ARM_IDENTIFIER}") - BRIDGES.append(f"2.0.31{ARM_IDENTIFIER}") - BRIDGES.append(f"2.0.30{ARM_IDENTIFIER}") else: BRIDGES.append("2.0.33") BRIDGES.append("2.0.32") - BRIDGES.append("2.0.31") - BRIDGES.append("2.0.27") - BRIDGES.append("2.0.26") - BRIDGES.append("2.0.19") def patch_emulators_for_nix(dir_to_patch: str = "") -> None: diff --git a/src/binaries/firmware/bin/arm/trezor-emu-core-T3T1-v2-main-arm b/src/binaries/firmware/bin/arm/trezor-emu-core-T3T1-v2-main-arm deleted file mode 100644 index c49605b..0000000 Binary files a/src/binaries/firmware/bin/arm/trezor-emu-core-T3T1-v2-main-arm and /dev/null differ diff --git a/src/binaries/firmware/bin/download.sh b/src/binaries/firmware/bin/download.sh index 19119cd..aecd337 100755 --- a/src/binaries/firmware/bin/download.sh +++ b/src/binaries/firmware/bin/download.sh @@ -5,35 +5,57 @@ SYSTEM_ARCH=$(uname -m) cd "$(dirname "${BASH_SOURCE[0]}")" BIN_DIR=$(pwd) - -# WARNING: this will download the emulators from the latest SUCCESSFULLY run pipeline from trezor-firmware. -# If the pipeline fails, it will download from the previous successful run. -GITLAB_URL="https://gitlab.com/satoshilabs/trezor/trezor-firmware/-/jobs/artifacts/main/download" +BASE_EMU_URL="https://data.trezor.io/dev/firmware/releases/emulators-new" if [[ $SYSTEM_ARCH == x86_64* ]]; then - # All core emulators are downloaded from trezor.io - SITE="https://data.trezor.io/dev/firmware/releases/emulators/" - LEGACY_LATEST_BUILD="${GITLAB_URL}?job=legacy%20emu%20regular%20debug%20build" - CUT_DIRS=4 + suffix="" elif [[ $SYSTEM_ARCH == aarch64* ]]; then - SITE="https://data.trezor.io/dev/firmware/releases/emulators/arm/" - CORE_LATEST_BUILD="${GITLAB_URL}?job=core%20unix%20frozen%20debug%20build%20arm" - R_LATEST_BUILD="${GITLAB_URL}?job=core%20unix%20frozen%20R%20debug%20build%20arm" - LEGACY_LATEST_BUILD="${GITLAB_URL}?job=legacy%20emu%20regular%20debug%20build%20arm" - T3T1_LATEST_BUILD="${GITLAB_URL}?job=core%20unix%20frozen%20T3T1%20debug%20build%20arm" - CUT_DIRS=5 + suffix="-arm" else echo "Not a supported arch - $SYSTEM_ARCH" exit 1 fi -if ! wget --no-config -e robots=off --no-verbose --no-clobber --no-parent --cut-dirs=$CUT_DIRS --no-host-directories --recursive --reject "index.html*" "$SITE"; then - echo "Unable to fetch released emulators from $SITE" - echo "You will have only available latest builds from CI" - echo - fi +# Define the emulators to download latest 10 versions +files=( + # T1B1 + "T1B1/trezor-emu-legacy-T1B1-v1.10.0${suffix}" + "T1B1/trezor-emu-legacy-T1B1-v1.10.1${suffix}" + "T1B1/trezor-emu-legacy-T1B1-v1.10.2${suffix}" + "T1B1/trezor-emu-legacy-T1B1-v1.10.3${suffix}" + "T1B1/trezor-emu-legacy-T1B1-v1.10.4${suffix}" + "T1B1/trezor-emu-legacy-T1B1-v1.10.5${suffix}" + "T1B1/trezor-emu-legacy-T1B1-v1.11.1${suffix}" + "T1B1/trezor-emu-legacy-T1B1-v1.11.2${suffix}" + "T1B1/trezor-emu-legacy-T1B1-v1.12.0${suffix}" + "T1B1/trezor-emu-legacy-T1B1-v1.12.1${suffix}" + # T2T1 + "T2T1/trezor-emu-core-T2T1-v2.5.1${suffix}" + "T2T1/trezor-emu-core-T2T1-v2.5.2${suffix}" + "T2T1/trezor-emu-core-T2T1-v2.5.3${suffix}" + "T2T1/trezor-emu-core-T2T1-v2.6.0${suffix}" + "T2T1/trezor-emu-core-T2T1-v2.6.3${suffix}" + "T2T1/trezor-emu-core-T2T1-v2.6.4${suffix}" + "T2T1/trezor-emu-core-T2T1-v2.7.0${suffix}" + "T2T1/trezor-emu-core-T2T1-v2.7.2${suffix}" + "T2T1/trezor-emu-core-T2T1-v2.8.1${suffix}" + # T2B1 + "T2B1/trezor-emu-core-T2B1-v2.6.3${suffix}" + "T2B1/trezor-emu-core-T2B1-v2.6.4${suffix}" + "T2B1/trezor-emu-core-T2B1-v2.7.0${suffix}" + "T2B1/trezor-emu-core-T2B1-v2.7.2${suffix}" + "T2B1/trezor-emu-core-T2B1-v2.8.0${suffix}" + # T3T1 + "T3T1/trezor-emu-core-T3T1-v2.7.2${suffix}" + "T3T1/trezor-emu-core-T3T1-v2.8.0${suffix}" + "T3T1/trezor-emu-core-T3T1-v2.8.1${suffix}" +) + +for file in "${files[@]}"; do + wget "${BASE_EMU_URL}/${file}" || true +done # download emulator from main TMP_DIR="$BIN_DIR/tmp" @@ -48,42 +70,32 @@ trap cleanup EXIT cd "$TMP_DIR" -# NOTE: when unziping, using -o to overwrite existing files, -# otherwise extracting TR into already existing TT will ask for confirmation - if [[ $SYSTEM_ARCH == x86_64* ]]; then - wget --no-config -O trezor-emu-legacy-main.zip "$LEGACY_LATEST_BUILD" - unzip -o -q trezor-emu-legacy-main.zip - mv legacy/firmware/trezor.elf ../trezor-emu-legacy-v1-main + wget https://data.trezor.io/dev/firmware/emu-nightly/trezor-emu-legacy-T1B1-universal + mv trezor-emu-legacy-T1B1-universal ../trezor-emu-legacy-T1B1-v1-main wget https://data.trezor.io/dev/firmware/emu-nightly/trezor-emu-core-T2T1-universal - mv trezor-emu-core-T2T1-universal ../trezor-emu-core-v2-main + mv trezor-emu-core-T2T1-universal ../trezor-emu-core-T2T1-v2-main + + wget https://data.trezor.io/dev/firmware/emu-nightly/trezor-emu-core-T2B1-universal + mv trezor-emu-core-T2B1-universal ../trezor-emu-core-T2B1-v2-main wget https://data.trezor.io/dev/firmware/emu-nightly/trezor-emu-core-T3T1-universal mv trezor-emu-core-T3T1-universal ../trezor-emu-core-T3T1-v2-main - wget https://data.trezor.io/dev/firmware/emu-nightly/trezor-emu-core-T2B1-universal - mv trezor-emu-core-T2B1-universal ../trezor-emu-core-R-v2-main - elif [[ $SYSTEM_ARCH == aarch64* ]]; then - wget --no-config -O trezor-emu-core-arm-main.zip "$CORE_LATEST_BUILD" - unzip -o -q trezor-emu-core-arm-main.zip -d arm/ - mv arm/core/build/unix/trezor-emu-core-arm ../trezor-emu-core-v2-main-arm - wget --no-config -O trezor-emu-core-R-arm-main.zip "$R_LATEST_BUILD" - unzip -o -q trezor-emu-core-R-arm-main.zip -d arm/ - mv arm/core/build/unix/trezor-emu-core-arm ../trezor-emu-core-R-v2-main-arm + wget https://data.trezor.io/dev/firmware/emu-nightly/trezor-emu-arm-legacy-T1B1-universal + mv trezor-emu-arm-legacy-T1B1-universal ../trezor-emu-legacy-T1B1-v1-main-arm - # TEMPORARILY replaced by baking the emulators into the image - wget --no-config -O trezor-emu-core-T3T1-arm-main.zip "$T3T1_LATEST_BUILD" - unzip -o -q trezor-emu-core-T3T1-arm-main.zip -d arm/ - mv arm/core/build/unix/trezor-emu-core-arm ../trezor-emu-core-T3T1-v2-main-arm - mv ../arm/trezor-emu-core-T3T1-v2-main-arm ../trezor-emu-core-T3T1-v2-main-static-arm + wget https://data.trezor.io/dev/firmware/emu-nightly/trezor-emu-arm-core-T2T1-universal + mv trezor-emu-arm-core-T2T1-universal ../trezor-emu-core-T2T1-v2-main-arm - wget --no-config -O trezor-emu-legacy-arm-main.zip "$LEGACY_LATEST_BUILD" - unzip -o -q trezor-emu-legacy-arm-main.zip -d arm/ - mv arm/legacy/firmware/trezor-arm.elf ../trezor-emu-legacy-v1-main-arm + wget https://data.trezor.io/dev/firmware/emu-nightly/trezor-emu-arm-core-T2B1-universal + mv trezor-emu-arm-core-T2B1-universal ../trezor-emu-core-T2B1-v2-main-arm + wget https://data.trezor.io/dev/firmware/emu-nightly/trezor-emu-arm-core-T3T1-universal + mv trezor-emu-arm-core-T3T1-universal ../trezor-emu-core-T3T1-v2-main-arm fi cd "$BIN_DIR" diff --git a/src/binaries/firmware/bin/download_latest_gh.py b/src/binaries/firmware/bin/download_latest_gh.py deleted file mode 100644 index 1486def..0000000 --- a/src/binaries/firmware/bin/download_latest_gh.py +++ /dev/null @@ -1,100 +0,0 @@ -import os -import shutil -import zipfile -from pathlib import Path -from tempfile import TemporaryDirectory - -import requests - -OWNER = "trezor" -REPO = "trezor-firmware" -GH_TOKEN = os.environ.get("GH_TOKEN") - -if not GH_TOKEN: - raise ValueError("GH_TOKEN is not set") - -# WORKFLOW_ID = ".github/workflows/core.yml" -WORKFLOW_ID = 75504717 -BRANCH = "main" -CORE_NAMES = [ - "core-emu-T2T1-universal-debuglink-noasan", - "core-emu-T2B1-universal-debuglink-noasan", - "core-emu-T3T1-universal-debuglink-noasan", -] - -HEADERS = { - "Accept": "application/vnd.github+json", - "Authorization": f"Bearer {GH_TOKEN}", -} - -# https://api.github.com/repos/trezor/trezor-firmware/actions/workflows -# https://api.github.com/repos/trezor/trezor-firmware/actions/workflows/75504717 -# https://api.github.com/repos/trezor/trezor-firmware/actions/workflows/75504717?event=schedule" -# https://api.github.com/repos/trezor/trezor-firmware/actions/runs/8514990723/artifacts -# https://api.github.com/repos/trezor/trezor-firmware/actions/artifacts/1375804284/zip - - -def get_all_workflows(): - url = f"https://api.github.com/repos/{OWNER}/{REPO}/actions/workflows" - return requests.get(url, headers=HEADERS).json() - - -def get_latest_run_id(workflow_id: int, branch: str) -> int | None: - url = f"https://api.github.com/repos/{OWNER}/{REPO}/actions/workflows/{workflow_id}/runs?event=schedule" - print("url", url) - response = requests.get(url, headers=HEADERS) - response.raise_for_status() - data = response.json() - runs = data.get("workflow_runs", []) - for run in runs: - # TODO: might also check run["conclusion"] == "success", but it seems to always fail - if run["head_branch"] == branch and run["status"] == "completed": - return run["id"] # type: ignore - return None - - -def download_artifacts(run_id: int, job_names: list[str]) -> None: - url = f"https://api.github.com/repos/{OWNER}/{REPO}/actions/runs/{run_id}/artifacts" - print("url", url) - response = requests.get(url, headers=HEADERS).json() - artifacts = response.get("artifacts", []) - for artifact in artifacts: - name = artifact["name"] - if job_names and name not in job_names: - continue - - # core-emu-T2T1-universal-debuglink-noasan - model_name = name.split("-")[2] - model_ident = { - "T2T1": "", - "T2B1": "-R", - "T3T1": "-T3T1", - }[model_name] - emu_name = f"trezor-emu-core{model_ident}-v2-main" - print("Downloading", emu_name) - - download_url = artifact["archive_download_url"] - print("download_url", download_url) - artifact_response = requests.get(download_url, headers=HEADERS) - - with TemporaryDirectory() as temp_dir: - zip_path = Path(temp_dir) / "archive.zip" - with open(zip_path, "wb") as file: - file.write(artifact_response.content) - with zipfile.ZipFile(zip_path, "r") as zip_ref: - zip_ref.extractall(temp_dir) - - source_file_path = Path(temp_dir) / Path("unix/trezor-emu-core") - shutil.copy(source_file_path, Path.cwd() / emu_name) - - -if __name__ == "__main__": - # workflows = get_all_workflows() - # print("workflows", workflows) - - run_id = get_latest_run_id(WORKFLOW_ID, BRANCH) - print("run_id", run_id) - if run_id: - download_artifacts(run_id, CORE_NAMES) - else: - print("No successful runs found on the main branch.") diff --git a/src/binaries/firmware/bin/patch-bin.sh b/src/binaries/firmware/bin/patch-bin.sh new file mode 100755 index 0000000..82e6a95 --- /dev/null +++ b/src/binaries/firmware/bin/patch-bin.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +SYSTEM_ARCH=$(uname -m) +echo "System architecture: $SYSTEM_ARCH" + +if [[ "$SYSTEM_ARCH" == "x86_64" ]]; then + INTERPRETER_DEFAULT="/lib64/ld-linux-x86-64.so.2" +elif [[ "$SYSTEM_ARCH" == "aarch64" || "$SYSTEM_ARCH" == "arm64" ]]; then + INTERPRETER_DEFAULT="/lib/ld-linux-aarch64.so.1" +else + echo "Unknown system architecture: $SYSTEM_ARCH" + exit 1 +fi + +get_interpreter() { + local arch="$1" + case "$arch" in + "Advanced Micro Devices X86-64"|"x86-64") + echo "/lib64/ld-linux-x86-64.so.2" + ;; + "AArch64") + echo "/lib/ld-linux-aarch64.so.1" + ;; + *) + echo "Unknown architecture: $arch" + return 1 + ;; + esac +} + +change_interpreter() { + local binary="$1" + local arch current_interpreter new_interpreter + + if ! readelf -h "$binary" &>/dev/null; then + echo "Skipping $binary: Not an ELF file." + return + fi + + arch=$(readelf -h "$binary" | awk -F: '/Machine:/ {print $2}' | xargs) + echo "Detected architecture for $binary: $arch" + + new_interpreter=$(get_interpreter "$arch") + + if [[ $? -ne 0 || -z "$new_interpreter" ]]; then + echo "Skipping $binary: Unsupported or unknown architecture." + return + fi + + current_interpreter=$(patchelf --print-interpreter "$binary" 2>/dev/null) + echo "Current interpreter for $binary: $current_interpreter" + + if [[ $? -eq 0 && "$current_interpreter" != "$new_interpreter" ]]; then + echo "Patching $binary (arch: $arch, old: $current_interpreter, new: $new_interpreter)" + patchelf --set-interpreter "$new_interpreter" "$binary" + else + echo "No need to patch $binary." + fi +} + +export -f change_interpreter +export -f get_interpreter +export INTERPRETER_DEFAULT + +BINARY_DIR="./" + +find "$BINARY_DIR" -type f -executable -exec bash -c 'change_interpreter "$0"' {} \; + +echo "All binaries patched." diff --git a/src/binaries/trezord-go/bin/download.sh b/src/binaries/trezord-go/bin/download.sh index 75efa85..317b856 100755 --- a/src/binaries/trezord-go/bin/download.sh +++ b/src/binaries/trezord-go/bin/download.sh @@ -14,5 +14,4 @@ else exit 1 fi -# Older bridge (<= 31) needs older glibc so we are pinning to nixos-21.05 (stable) as of 2021-07-02 -nix-shell -p autoPatchelfHook -I "nixpkgs=https://github.com/NixOS/nixpkgs/archive/e9148dc1c30e02aae80cc52f68ceb37b772066f3.tar.gz" --run "autoPatchelf $FILES" +# nix-shell --run "autoPatchelf $FILES" diff --git a/src/binaries/trezord-go/bin/trezord-go-v2.0.19 b/src/binaries/trezord-go/bin/trezord-go-v2.0.19 deleted file mode 100755 index 145cafb..0000000 Binary files a/src/binaries/trezord-go/bin/trezord-go-v2.0.19 and /dev/null differ diff --git a/src/binaries/trezord-go/bin/trezord-go-v2.0.26 b/src/binaries/trezord-go/bin/trezord-go-v2.0.26 deleted file mode 100755 index 21b2cb9..0000000 Binary files a/src/binaries/trezord-go/bin/trezord-go-v2.0.26 and /dev/null differ diff --git a/src/binaries/trezord-go/bin/trezord-go-v2.0.27 b/src/binaries/trezord-go/bin/trezord-go-v2.0.27 deleted file mode 100755 index 452b0d2..0000000 Binary files a/src/binaries/trezord-go/bin/trezord-go-v2.0.27 and /dev/null differ diff --git a/src/binaries/trezord-go/bin/trezord-go-v2.0.30-arm b/src/binaries/trezord-go/bin/trezord-go-v2.0.30-arm deleted file mode 100755 index 4799962..0000000 Binary files a/src/binaries/trezord-go/bin/trezord-go-v2.0.30-arm and /dev/null differ diff --git a/src/binaries/trezord-go/bin/trezord-go-v2.0.31 b/src/binaries/trezord-go/bin/trezord-go-v2.0.31 deleted file mode 100755 index c259d0a..0000000 Binary files a/src/binaries/trezord-go/bin/trezord-go-v2.0.31 and /dev/null differ diff --git a/src/binaries/trezord-go/bin/trezord-go-v2.0.31-arm b/src/binaries/trezord-go/bin/trezord-go-v2.0.31-arm deleted file mode 100755 index 43a1a0e..0000000 Binary files a/src/binaries/trezord-go/bin/trezord-go-v2.0.31-arm and /dev/null differ diff --git a/src/bridge.py b/src/bridge.py index d5a6d39..8abd755 100644 --- a/src/bridge.py +++ b/src/bridge.py @@ -4,6 +4,7 @@ import os import socket import time +from shlex import split from typing import TYPE_CHECKING from psutil import Popen @@ -97,14 +98,14 @@ def start(version: str, proxy: bool = False, output_to_logfile: bool = True) -> if helpers.physical_trezor() else f"{bridge_location} -ed 21324:21325 -u=false" ) - # Conditionally redirecting the output to a logfile instead of terminal/stdout + command_list = split(command) if output_to_logfile: log_file = open(helpers.EMU_BRIDGE_LOG, "a") log(f"All the bridge debug output redirected to {helpers.EMU_BRIDGE_LOG}") - BRIDGE = Popen(command, shell=True, stdout=log_file, stderr=log_file) + BRIDGE = Popen(command_list, stdout=log_file, stderr=log_file) else: - BRIDGE = Popen(command, shell=True) + BRIDGE = Popen(command_list) log(f"Bridge spawned: {BRIDGE}. CMD: {BRIDGE.cmdline()}") @@ -129,10 +130,22 @@ def stop(proxy: bool = True) -> None: global VERSION_RUNNING if BRIDGE is None: - log("WARNING: Attempting to stop a brige, but it is not running", "red") + log("WARNING: Attempting to stop a bridge, but it is not running", "red") else: - BRIDGE.kill() - log(f"Bridge killed: {BRIDGE}") + try: + BRIDGE.terminate() + BRIDGE.wait(timeout=5) + except Exception as e: + log(f"Termination failed: {e}, killing process.", "yellow") + BRIDGE.kill() + BRIDGE.wait() + + # Ensuring all child processes are cleaned up + for child in BRIDGE.children(recursive=True): + log(f"Killing child process {child.pid}") + child.kill() + child.wait() + BRIDGE = None VERSION_RUNNING = None diff --git a/src/controller.py b/src/controller.py index 816d55d..25ac013 100644 --- a/src/controller.py +++ b/src/controller.py @@ -46,7 +46,7 @@ class BackgroundCheckResponse(TypedDict): user="rpc", passwd="rpc", ) -PREV_RUNNING_MODEL: str = "" +PREV_RUNNING_MODEL: binaries.Model | None = None def is_regtest_active() -> bool: @@ -162,30 +162,26 @@ def run_bridge_command(self) -> "ResponseType": def run_emulator_command(self) -> "ResponseType": global PREV_RUNNING_MODEL - # The versions are sorted, the first one is the current main - # build and then the rest by version number. - # Not supplying any version will result in "2-main", - # "X-latest" will get the latest release of X. if self.command == "emulator-start": + model: binaries.Model | None = self.request_dict.get("model") + if not model: + return { + "success": False, + "error": "Model must be supplied for the emulator to start", + } + binaries.check_model(model) + + # Not supplying any version will result in "-main", + # "-latest" will get the latest release of X. if "version" in self.request_dict: requested_version = self.request_dict["version"] if requested_version.endswith("-latest"): - model = requested_version[0] version = binaries.get_latest_release_version(model) else: version = requested_version else: - version = binaries.get_main_version("2") + version = binaries.get_main_version(model) - # TEMPORARY: translating `-master` to `-main` because of the firmware branch change - # TODO: revert this after some time - version = version.replace("-master", "-main") - - # Model is not compulsory for backwards compatibility purposes - # Is needed now, because TR and TT are sharing the same versions - # (default to the first character in version, which works fine for - # "legacy" T1 and TT setup - `2.5.0`, `2-main`, `1.10.1`, etc.) - model = self.request_dict.get("model", version[0]) wipe = self.request_dict.get("wipe", False) output_to_logfile = self.request_dict.get("output_to_logfile", True) save_screenshots = self.request_dict.get("save_screenshots", False) @@ -206,6 +202,12 @@ def run_emulator_command(self) -> "ResponseType": elif self.command == "emulator-start-from-url": url = self.request_dict["url"] model = self.request_dict["model"] + if not model: + return { + "success": False, + "error": "Model must be supplied for the emulator to start", + } + binaries.check_model(model) wipe = self.request_dict.get("wipe", False) output_to_logfile = self.request_dict.get("output_to_logfile", True) save_screenshots = self.request_dict.get("save_screenshots", False) diff --git a/src/dashboard/js/index.js b/src/dashboard/js/index.js index 8b61910..ce35ba2 100644 --- a/src/dashboard/js/index.js +++ b/src/dashboard/js/index.js @@ -16,23 +16,23 @@ const app = createApp({ }, emulators: { versions: { - 1: { + T1B1: { header: "Trezor One", versions: [], selected: "", }, - 2: { + T2T1: { header: "Trezor T", versions: [], selected: "", }, - R: { + T2B1: { header: "Trezor Safe 3", versions: [], selected: "", }, T3T1: { - header: "Trezor T3T1", + header: "Trezor Safe 5", versions: [], selected: "", }, @@ -47,7 +47,7 @@ const app = createApp({ url: "", typeChoices: ["Gitlab job ID", "Custom link"], type: "Gitlab job ID", - model: "2", + model: "T2T1", downloadMessage: "", }, emulatorCommands: { @@ -274,7 +274,7 @@ const app = createApp({ const T2PathSuffix = "artifacts/raw/core/build/unix/trezor-emu-core"; const baseUrl = `${gitlabJobPrefix}/${url}`; - if (model === "1") { + if (model === "T1B1") { url = `${baseUrl}/${T1PathSuffix}`; } else { url = `${baseUrl}/${T2PathSuffix}`; diff --git a/src/emulator.py b/src/emulator.py index 79f4d5c..c7224d9 100644 --- a/src/emulator.py +++ b/src/emulator.py @@ -124,12 +124,7 @@ def start_from_url( # Deciding the location to save depending on being T1/TT/TR # (to be compatible with already existing emulators) - model_identifier = { - "1": binaries.IDENTIFIER_T1, - "2": binaries.IDENTIFIER_TT, - "R": binaries.IDENTIFIER_TR, - "T3T1": binaries.IDENTIFIER_T3T1, - }.get(model) + model_identifier = binaries.MODEL_IDENTIFIERS.get(model) if not model_identifier: raise RuntimeError(f"Unknown model {model}") emu_path = binaries.USER_DOWNLOADED_DIR / f"{model_identifier}{emu_name}" @@ -215,13 +210,13 @@ def version_model() -> str: emu_location = Path(binaries.get_firmware_location(model, version)) - if model in ("2", "R", "T3T1"): + if model in ("T2T1", "T2B1", "T3T1"): EMULATOR = CoreEmulator( emu_location, profile_dir=binaries.FIRMWARE_BIN_DIR, logfile=logfile, ) - elif model == "1": + elif model == "T1B1": os.environ["TREZOR_OLED_SCALE"] = str(TREZOR_ONE_OLED_SCALE) EMULATOR = LegacyEmulator( emu_location, @@ -259,7 +254,7 @@ def version_model() -> str: # Creating a new directory for each emulator session, so the screens are not being overwritten # NOT supported for T1, as it needs Debuglink for screenshot creation, and therefore # it would not save screenshots for example from Suite and other interaction. - if save_screenshots and model != "1": + if save_screenshots and model != "T1B1": time.sleep(SLEEP) with connect_to_debuglink() as debug: # Need to set model info (both TT and TR are under same category) diff --git a/tests/controller_test.py b/tests/controller_test.py index b8b1c88..6b75486 100644 --- a/tests/controller_test.py +++ b/tests/controller_test.py @@ -181,19 +181,19 @@ async def test_bridge_start_stop(websocket) -> None: async def test_emulator_tt_start_stop(websocket) -> None: await _test_start_stop( - websocket, component="emulator", version=EMU_TO_TEST_TT, model="2" + websocket, component="emulator", version=EMU_TO_TEST_TT, model="T2T1" ) async def test_emulator_t1_start_stop(websocket) -> None: await _test_start_stop( - websocket, component="emulator", version=EMU_TO_TEST_T1, model="1" + websocket, component="emulator", version=EMU_TO_TEST_T1, model="T1B1" ) async def test_emulator_tr_start_stop(websocket) -> None: await _test_start_stop( - websocket, component="emulator", version=EMU_TO_TEST_TR, model="R" + websocket, component="emulator", version=EMU_TO_TEST_TR, model="T2B1" )