From d4f62df23c1fa964f673c1c607c8add4fcdc2c25 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 21 Jan 2026 11:36:12 +0100 Subject: [PATCH 1/9] ci: drop cirrus leftovers --- README.md | 2 +- ci/cirrus.Dockerfile | 99 -------------------------------------------- ci/py310.Dockerfile | 2 +- ci/py311.Dockerfile | 2 +- 4 files changed, 3 insertions(+), 102 deletions(-) delete mode 100644 ci/cirrus.Dockerfile diff --git a/README.md b/README.md index c80374b18..ec1f8fab2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Bitcoin Hardware Wallet Interface -[![Build Status](https://api.cirrus-ci.com/github/bitcoin-core/HWI.svg)](https://cirrus-ci.com/github/bitcoin-core/HWI) +[![CI](https://github.com/bitcoin-core/HWI/actions/workflows/ci.yml/badge.svg)](https://github.com/bitcoin-core/HWI/actions/workflows/ci.yml) [![Documentation Status](https://readthedocs.org/projects/hwi/badge/?version=latest)](https://hwi.readthedocs.io/en/latest/?badge=latest) The Bitcoin Hardware Wallet Interface is a Python library and command line tool for interacting with hardware wallets. diff --git a/ci/cirrus.Dockerfile b/ci/cirrus.Dockerfile deleted file mode 100644 index 07c201c6e..000000000 --- a/ci/cirrus.Dockerfile +++ /dev/null @@ -1,99 +0,0 @@ -# Cache break (modify this line to break cirrus' dockerfile build cache) 1 - -FROM python:3.9 - -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get update -RUN apt-get install -y \ - autotools-dev \ - automake \ - bsdmainutils \ - build-essential \ - ccache \ - clang \ - cmake \ - curl \ - cython3 \ - gcc-arm-none-eabi \ - gcc-arm-linux-gnueabihf \ - git \ - libboost-system-dev \ - libboost-filesystem-dev \ - libboost-chrono-dev \ - libboost-test-dev \ - libboost-thread-dev \ - libc6-dev-armhf-cross \ - libdb-dev \ - libdb++-dev \ - libevent-dev \ - libgcrypt20-dev \ - libnewlib-arm-none-eabi \ - libpcsclite-dev \ - libsdl2-dev \ - libsdl2-image-dev \ - libssl-dev \ - libslirp-dev \ - libtool \ - libudev-dev \ - libusb-1.0-0-dev \ - ninja-build \ - pkg-config \ - qemu-user-static \ - swig - -RUN pip install poetry flake8 -RUN wget https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init -RUN chmod +x rustup-init && ./rustup-init -y -ENV PATH="/root/.cargo/bin:$PATH" -RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v22.0/protoc-22.0-linux-x86_64.zip -RUN unzip protoc-22.0-linux-x86_64.zip -d /usr/local -RUN protoc --version - -#################### -# Local build/test steps -# ----------------- -# To install all simulators/tests locally, uncomment the block below, -# then build the docker image and interactively run the tests -# as needed. -# e.g., -# docker build -f ci/cirrus.Dockerfile -t hwi_test . -# docker run -it --entrypoint /bin/bash hwi_test -# cd test; poetry run ./run_tests.py --ledger --coldcard --interface=cli --device-only -# For BitBox02: -# docker build -f ci/cirrus.Dockerfile -t hwi_test . -# ./ci/build_bitbox02.sh -# docker run -it -v bitbox02_volume:/test/work/bitbox02-firmware --name hwi --entrypoint /bin/bash hwi_test -# cd test; poetry run ./run_tests.py --bitbox02 --interface=cli --device-only -#################### - -#################### -#ENV EMAIL=email -#COPY pyproject.toml pyproject.toml -#RUN poetry run pip install construct pyelftools mnemonic jsonschema -# -## Set up environments first to take advantage of layer caching -#RUN mkdir test -#COPY test/setup_environment.sh test/setup_environment.sh -#COPY test/data/coldcard-multisig.patch test/data/coldcard-multisig.patch -## One by one to allow for intermediate caching of successful builds -#RUN cd test; ./setup_environment.sh --trezor-1 -#RUN cd test; ./setup_environment.sh --trezor-t -#RUN cd test; ./setup_environment.sh --coldcard -#RUN cd test; ./setup_environment.sh --bitbox01 -#RUN cd test; ./setup_environment.sh --ledger -#RUN cd test; ./setup_environment.sh --keepkey -#RUN cd test; ./setup_environment.sh --jade -#RUN cd test; ./setup_environment.sh --bitcoind -# -## Once everything has been built, put rest of files in place -## which have higher turn-over. -#COPY test/ test/ -#COPY hwi.py hwi-qt.py README.md / -#COPY hwilib/ /hwilib/ -#RUN poetry install -# -#################### - -ENV LC_ALL=C.UTF-8 -ENV LANG=C.UTF-8 -ENV LANGUAGE=C.UTF-8 diff --git a/ci/py310.Dockerfile b/ci/py310.Dockerfile index 299b44333..20106aa24 100644 --- a/ci/py310.Dockerfile +++ b/ci/py310.Dockerfile @@ -1,4 +1,4 @@ -# Cache break (modify this line to break cirrus' dockerfile build cache) 1 +# Cache break (modify this line to break the dockerfile build cache) 1 FROM python:3.10 diff --git a/ci/py311.Dockerfile b/ci/py311.Dockerfile index ab80e889a..cf97b42cf 100644 --- a/ci/py311.Dockerfile +++ b/ci/py311.Dockerfile @@ -1,4 +1,4 @@ -# Cache break (modify this line to break cirrus' dockerfile build cache) +# Cache break (modify this line to break the dockerfile build cache) FROM python:3.11 From 659a1437797860feb805993a1260fb31ef159756 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 21 Jan 2026 11:29:34 +0100 Subject: [PATCH 2/9] ci: drop unused Python 3.7 Docker file Also drops Python 3.6 dataclasses leftover. --- ci/py37.Dockerfile | 23 ----------------------- poetry.lock | 2 +- pyproject.toml | 1 - setup.py | 3 +-- 4 files changed, 2 insertions(+), 27 deletions(-) delete mode 100644 ci/py37.Dockerfile diff --git a/ci/py37.Dockerfile b/ci/py37.Dockerfile deleted file mode 100644 index fc8b67781..000000000 --- a/ci/py37.Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# Cache break (modify this line to break cirrus' dockerfile build cache) 1 - -FROM python:3.7 - -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get update -RUN apt-get install -y \ - cython3 \ - git \ - libpcsclite-dev \ - libsdl2-dev \ - libsdl2-image-dev \ - libslirp-dev \ - libudev-dev \ - libusb-1.0-0-dev \ - qemu-user-static \ - swig - -RUN pip install poetry flake8 - -ENV LC_ALL=C.UTF-8 -ENV LANG=C.UTF-8 -ENV LANGUAGE=C.UTF-8 diff --git a/poetry.lock b/poetry.lock index 88af47e0e..a11e72cbb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1311,4 +1311,4 @@ qt = ["pyside2"] [metadata] lock-version = "2.1" python-versions = "^3.9,<3.13" -content-hash = "ffa2aebd594ec1a5db15d7325c7bb26036b593faccf363163379e276bff1c191" +content-hash = "278dc524a94cc0c503225e4a20f353bd09fa3deaab956e9d559c3795a304ad65" diff --git a/pyproject.toml b/pyproject.toml index a615cb17d..6a24b95f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,6 @@ libusb1 = ">=1.7,<4" pyside2 = { version = "^5.14.0", optional = true, python = "<3.10" } cbor2 = ">=5.4.6,<5.8" pyserial = "^3.5" -dataclasses = {version = "^0.8", python = ">=3.6,<3.7"} semver = "^3.0.1" noiseprotocol = "^0.3.1" protobuf = "^4.23.3" diff --git a/setup.py b/setup.py index 6d301e0f6..f5dd544a7 100644 --- a/setup.py +++ b/setup.py @@ -39,8 +39,7 @@ 'typing-extensions>=4.4,<5.0'] extras_require = \ -{':python_version >= "3.6" and python_version < "3.7"': ['dataclasses>=0.8,<0.9'], - 'qt:python_version < "3.10"': ['pyside2>=5.14.0,<6.0.0']} +{'qt:python_version < "3.10"': ['pyside2>=5.14.0,<6.0.0']} entry_points = \ {'console_scripts': ['hwi = hwilib._cli:main', 'hwi-qt = hwilib._gui:main']} From dbb39df055ae1e473c4fcd70640fe465a6bea11b Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 21 Jan 2026 13:08:59 +0100 Subject: [PATCH 3/9] build: add docker / container ignore files --- .containerignore | 11 +++++++++++ .dockerignore | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 .containerignore create mode 100644 .dockerignore diff --git a/.containerignore b/.containerignore new file mode 100644 index 000000000..895b58026 --- /dev/null +++ b/.containerignore @@ -0,0 +1,11 @@ +# Exclude local build artifacts and any symlinks outside the context +build* + +# Local documentation files (can be symlinks outside the context) +*.md + +# Common Python / tooling artifacts +__pycache__/ +*.pyc +.venv/ +dist/ diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..895b58026 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +# Exclude local build artifacts and any symlinks outside the context +build* + +# Local documentation files (can be symlinks outside the context) +*.md + +# Common Python / tooling artifacts +__pycache__/ +*.pyc +.venv/ +dist/ From 4a0b8f2f88fdbbe2468582d45f364fb5aac06e96 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 21 Jan 2026 13:10:32 +0100 Subject: [PATCH 4/9] build: use Podman friendly syntax --- contrib/build-wine.Dockerfile | 2 -- contrib/build.Dockerfile | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/contrib/build-wine.Dockerfile b/contrib/build-wine.Dockerfile index 335b580bb..d781dfdcd 100644 --- a/contrib/build-wine.Dockerfile +++ b/contrib/build-wine.Dockerfile @@ -1,7 +1,5 @@ FROM debian:bookworm-slim -SHELL ["/bin/bash", "-c"] - ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update RUN apt-get install -y \ diff --git a/contrib/build.Dockerfile b/contrib/build.Dockerfile index 4e1025352..766d5b0d1 100644 --- a/contrib/build.Dockerfile +++ b/contrib/build.Dockerfile @@ -1,7 +1,5 @@ FROM debian:bookworm-slim -SHELL ["/bin/bash", "-c"] - ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update RUN apt-get install -y \ @@ -38,7 +36,7 @@ COPY contrib/reproducible-python.diff /opt/reproducible-python.diff ENV PYTHON_CONFIGURE_OPTS="--enable-shared" ENV BUILD_DATE="Jan 1 2019" ENV BUILD_TIME="00:00:00" -RUN eval "$(pyenv init --path)" && eval "$(pyenv virtualenv-init -)" && cat /opt/reproducible-python.diff | pyenv install -kp 3.9.19 +RUN /bin/bash -c 'eval "$(pyenv init --path)" && eval "$(pyenv virtualenv-init -)" && cat /opt/reproducible-python.diff | pyenv install -kp 3.9.19' ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8 From d2ef50689365abd8b296de66707fdd80496ba878 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 21 Jan 2026 13:11:13 +0100 Subject: [PATCH 5/9] build: deterministic builds use Python 3.10 --- contrib/build.Dockerfile | 4 ++-- contrib/build_bin.sh | 7 ++++-- contrib/build_dist.sh | 7 ++++-- contrib/build_wine.sh | 33 ++++++++++++++-------------- docs/development/release-process.rst | 12 +++++----- poetry.lock | 6 ++--- pyproject.toml | 2 +- setup.py | 2 +- 8 files changed, 39 insertions(+), 34 deletions(-) diff --git a/contrib/build.Dockerfile b/contrib/build.Dockerfile index 766d5b0d1..d8f23da81 100644 --- a/contrib/build.Dockerfile +++ b/contrib/build.Dockerfile @@ -34,9 +34,9 @@ ENV PATH="$PYENV_ROOT/bin:$PATH" COPY contrib/reproducible-python.diff /opt/reproducible-python.diff ENV PYTHON_CONFIGURE_OPTS="--enable-shared" -ENV BUILD_DATE="Jan 1 2019" +ENV BUILD_DATE="Jan 1 2026" ENV BUILD_TIME="00:00:00" -RUN /bin/bash -c 'eval "$(pyenv init --path)" && eval "$(pyenv virtualenv-init -)" && cat /opt/reproducible-python.diff | pyenv install -kp 3.9.19' +RUN /bin/bash -c 'eval "$(pyenv init --path)" && eval "$(pyenv virtualenv-init -)" && cat /opt/reproducible-python.diff | pyenv install -kp 3.10.16' ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8 diff --git a/contrib/build_bin.sh b/contrib/build_bin.sh index 2db94f439..c9c9ec597 100755 --- a/contrib/build_bin.sh +++ b/contrib/build_bin.sh @@ -6,8 +6,11 @@ set -ex ARCH=$(uname -m | tr '[:upper:]' '[:lower:]') +PYTHON_VERSION=3.10.16 + eval "$(pyenv init --path)" eval "$(pyenv virtualenv-init -)" +export PYENV_VERSION="$PYTHON_VERSION" pip install -U pip pip install poetry @@ -21,8 +24,8 @@ else fi # We also need to change the timestamps of all of the base library files -lib_dir=$(pyenv prefix)/lib/python3.9 -TZ=UTC find ${lib_dir} -name '*.py' -type f -execdir touch -t "201901010000.00" '{}' \; +lib_dir=$(pyenv prefix)/lib/python3.10 +TZ=UTC find ${lib_dir} -name '*.py' -type f -execdir touch -t "202601010000.00" '{}' \; # Make the standalone binary export PYTHONHASHSEED=42 diff --git a/contrib/build_dist.sh b/contrib/build_dist.sh index e15b9e5d9..825c6d10d 100755 --- a/contrib/build_dist.sh +++ b/contrib/build_dist.sh @@ -4,8 +4,11 @@ set -ex +PYTHON_VERSION=3.10.16 + eval "$(pyenv init --path)" eval "$(pyenv virtualenv-init -)" +export PYENV_VERSION="$PYTHON_VERSION" pip install -U pip pip install poetry @@ -20,5 +23,5 @@ fi # Make the distribution archives for pypi poetry build -f wheel -# faketime is needed to make sdist detereministic -TZ=UTC faketime -f "2019-01-01 00:00:00" poetry build -f sdist +# faketime is needed to make sdist deterministic +TZ=UTC faketime -f "2026-01-01 00:00:00" poetry build -f sdist diff --git a/contrib/build_wine.sh b/contrib/build_wine.sh index 64d211f97..30efca207 100755 --- a/contrib/build_wine.sh +++ b/contrib/build_wine.sh @@ -3,7 +3,11 @@ set -ex -PYTHON_VERSION=3.9.13 +# Note: Python MSIs/EXEs are no longer hosted for 3.10.x on python.org. +# The NuGet python package currently only goes up to 3.10.11, so Windows builds use that. +PYTHON_VERSION=3.10.11 +PYTHON_NUGET_URL="https://api.nuget.org/v3-flatcontainer/python/${PYTHON_VERSION}/python.${PYTHON_VERSION}.nupkg" +PYTHON_NUGET_HASH="7c6f99b160a36a7e09492dfcff2b0a3a60bb5229ca44cdcc3ecb32871a6144d0" PYTHON_FOLDER="python3" PYHOME="c:/$PYTHON_FOLDER" @@ -19,20 +23,15 @@ WINDOWS_SDK_VERSION=10.0.17763.0 wine 'wineboot' -# Install Python -# Get the PGP keys -wget -O pubkeys.txt -N -c "https://keybase.io/stevedower/pgp_keys.asc?fingerprint=7ed10b6531d7c8e1bc296021fc624643487034e5" -gpg --import pubkeys.txt -rm pubkeys.txt - -# Install python components -for msifile in core dev exe lib pip tools; do - wget -N -c "https://www.python.org/ftp/python/$PYTHON_VERSION/amd64/${msifile}.msi" - wget -N -c "https://www.python.org/ftp/python/$PYTHON_VERSION/amd64/${msifile}.msi.asc" - gpg --verify "${msifile}.msi.asc" "${msifile}.msi" - wine msiexec /i "${msifile}.msi" /qb TARGETDIR=$PYHOME - rm $msifile.msi* -done +# Install Python from NuGet package +wget -O python.nupkg -N -c "$PYTHON_NUGET_URL" +echo "$PYTHON_NUGET_HASH python.nupkg" | sha256sum -c +rm -rf python-nupkg +7z x python.nupkg -opython-nupkg >/dev/null +rm -rf ~/.wine/drive_c/python3 +mkdir -p ~/.wine/drive_c/python3 +cp -a python-nupkg/tools/* ~/.wine/drive_c/python3/ +rm -rf python.nupkg python-nupkg # Get and build libusb wget -N -c -O libusb.tar.bz2 "$LIBUSB_URL" @@ -40,7 +39,7 @@ echo "$LIBUSB_HASH libusb.tar.bz2" | sha256sum -c tar -xf libusb.tar.bz2 pushd "libusb-$LIBUSB_VERSION" ./configure --host=x86_64-w64-mingw32 -faketime -f "2019-01-01 00:00:00" make +faketime -f "2026-01-01 00:00:00" make cp libusb/.libs/libusb-1.0.dll ~/.wine/drive_c/python3/ popd rm -r libusb* @@ -62,7 +61,7 @@ $PYTHON -m pip install poetry # We also need to change the timestamps of all of the base library files lib_dir=~/.wine/drive_c/python3/Lib -TZ=UTC find ${lib_dir} -name '*.py' -type f -execdir touch -t "201901010000.00" '{}' \; +TZ=UTC find ${lib_dir} -name '*.py' -type f -execdir touch -t "202601010000.00" '{}' \; # Install python dependencies POETRY="wine $PYHOME/Scripts/poetry.exe" diff --git a/docs/development/release-process.rst b/docs/development/release-process.rst index fb739ffdc..d6e97bd1d 100644 --- a/docs/development/release-process.rst +++ b/docs/development/release-process.rst @@ -4,7 +4,7 @@ Release Process 1. Bump version number in ``pyproject.toml`` and ``hwilib/__init__.py``, generate the setup.py file, and git tag release 2. Build distribution archives for PyPi with ``contrib/build_dist.sh`` 3. For MacOS and Linux, use ``contrib/build_bin.sh``. This needs to be run on a macOS machine for the macOS binary and on a Linux machine for the linux one. -4. For Windows, use ``contrib/build_wine.sh`` to build the Windows binary using wine +4. For Windows, use ``contrib/build_wine.sh`` to build the Windows binary using wine. Note that this uses Python 3.10.11 via the NuGet package because python.org no longer hosts the MSIs for newer 3.10.x releases. 5. Make ``SHA256SUMS.txt`` using ``contrib/make_shasums.sh``. 6. Make ``SHA256SUMS.txt.asc`` using ``gpg --clearsign SHA256SUMS.txt`` 7. Upload distribution archives to PyPi @@ -26,7 +26,7 @@ Build everything:: docker run -it --name hwi-builder -v $PWD:/opt/hwi --rm --workdir /opt/hwi hwi-builder /bin/bash -c "contrib/build_bin.sh && contrib/build_dist.sh" docker run -it --name hwi-wine-builder -v $PWD:/opt/hwi --rm --workdir /opt/hwi hwi-wine-builder /bin/bash -c "contrib/build_wine.sh" - docker run --platform linux/arm64 -it --rm --name hwi-builder-arm64 -v $PWD:/opt/hwi --workdir /opt/hwi hwi-builder-arm64 /bin/bash -c "contrib/build_bin.sh --without-gui && contrib/build_dist.sh --without-gui" + docker run --platform linux/arm64 -it --rm --name hwi-builder-arm64 -v $PWD:/opt/hwi --workdir /opt/hwi hwi-builder-arm64 /bin/bash -c "contrib/build_bin.sh --without-gui && contrib/build_dist.sh --without-gui" Building macOS binary ===================== @@ -35,14 +35,14 @@ Note that the macOS build is non-deterministic. First install `pyenv `_ using whichever method you prefer. -Then a deterministic build of Python 3.9.19 needs to be installed. This can be done with the patch in ``contrib/reproducible-python.diff``. First ``cd`` into HWI's source tree. Then use:: +Then a deterministic build of Python 3.10.16 needs to be installed. This can be done with the patch in ``contrib/reproducible-python.diff``. First ``cd`` into HWI's source tree. Then use:: - cat contrib/reproducible-python.diff | PYTHON_CONFIGURE_OPTS="--enable-framework" BUILD_DATE="Jan 1 2019" BUILD_TIME="00:00:00" pyenv install -kp 3.9.19 + cat contrib/reproducible-python.diff | PYTHON_CONFIGURE_OPTS="--enable-framework" BUILD_DATE="Jan 1 2026" BUILD_TIME="00:00:00" pyenv install -kp 3.10.16 -Make sure that python 3.9.19 is active:: +Make sure that python 3.10.16 is active:: $ python --version - Python 3.9.19 + Python 3.10.16 Now install `Poetry `_ with ``pip install poetry`` diff --git a/poetry.lock b/poetry.lock index a11e72cbb..096580a5f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -933,7 +933,7 @@ description = "Python bindings for the Qt cross-platform application and UI fram optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <3.11" groups = ["main"] -markers = "python_version == \"3.9\" and extra == \"qt\"" +markers = "python_version < \"3.11\" and extra == \"qt\"" files = [ {file = "PySide2-5.15.2.1-5.15.2-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:b5e1d92f26b0bbaefff67727ccbb2e1b577f2c0164b349b3d6e80febb4c5bde2"}, {file = "PySide2-5.15.2.1-5.15.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:235240b6ec8206d9fdf0232472c6ef3241783d480425e5b54796f06e39ed23da"}, @@ -1017,7 +1017,7 @@ description = "Python / C++ bindings helper module" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <3.11" groups = ["main"] -markers = "python_version == \"3.9\" and extra == \"qt\"" +markers = "python_version < \"3.11\" and extra == \"qt\"" files = [ {file = "shiboken2-5.15.2.1-5.15.2-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:f890f5611ab8f48b88cfecb716da2ac55aef99e2923198cefcf781842888ea65"}, {file = "shiboken2-5.15.2.1-5.15.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87079c07587859a525b9800d60b1be971338ce9b371d6ead81f15ee5a46d448b"}, @@ -1311,4 +1311,4 @@ qt = ["pyside2"] [metadata] lock-version = "2.1" python-versions = "^3.9,<3.13" -content-hash = "278dc524a94cc0c503225e4a20f353bd09fa3deaab956e9d559c3795a304ad65" +content-hash = "7409ca07c43208b622b22c5ae02b79cf9383c08c06302748c8d5c6ecbcf6f31c" diff --git a/pyproject.toml b/pyproject.toml index 6a24b95f0..bfae95f03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ pyaes = "^1.6" mnemonic = "~0" typing-extensions = "^4.4" libusb1 = ">=1.7,<4" -pyside2 = { version = "^5.14.0", optional = true, python = "<3.10" } +pyside2 = { version = "^5.15.2.1", optional = true, python = "<3.11" } cbor2 = ">=5.4.6,<5.8" pyserial = "^3.5" semver = "^3.0.1" diff --git a/setup.py b/setup.py index f5dd544a7..0761871c8 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ 'typing-extensions>=4.4,<5.0'] extras_require = \ -{'qt:python_version < "3.10"': ['pyside2>=5.14.0,<6.0.0']} +{'qt:python_version < "3.11"': ['pyside2>=5.15.2.1,<6.0.0']} entry_points = \ {'console_scripts': ['hwi = hwilib._cli:main', 'hwi-qt = hwilib._gui:main']} From 16cdc87830bba586111aaf5670cd8f727d39bf86 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 21 Jan 2026 13:22:14 +0100 Subject: [PATCH 6/9] Drop Python 3.9 support --- .github/workflows/device-test.yml | 2 +- .python-version | 2 +- ci/py39.Dockerfile | 23 -------------- poetry.lock | 51 +++---------------------------- pyproject.toml | 2 +- setup.py | 2 +- test/setup_environment.sh | 2 +- 7 files changed, 10 insertions(+), 74 deletions(-) delete mode 100644 ci/py39.Dockerfile diff --git a/.github/workflows/device-test.yml b/.github/workflows/device-test.yml index 5232314e7..5ec9ede76 100644 --- a/.github/workflows/device-test.yml +++ b/.github/workflows/device-test.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.9', '3.10', '3.11', '3.12'] + python-version: ['3.10', '3.11', '3.12'] device: - ${{ inputs.device }} test: diff --git a/.python-version b/.python-version index bd28b9c5c..c8cfe3959 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.9 +3.10 diff --git a/ci/py39.Dockerfile b/ci/py39.Dockerfile deleted file mode 100644 index 9c6f5eeb8..000000000 --- a/ci/py39.Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# Cache break (modify this line to break cirrus' dockerfile build cache) 1 - -FROM python:3.9 - -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get update -RUN apt-get install -y \ - cython3 \ - git \ - libpcsclite-dev \ - libsdl2-dev \ - libsdl2-image-dev \ - libslirp-dev \ - libudev-dev \ - libusb-1.0-0-dev \ - qemu-user-static \ - swig - -RUN pip install poetry flake8 - -ENV LC_ALL=C.UTF-8 -ENV LANG=C.UTF-8 -ENV LANGUAGE=C.UTF-8 diff --git a/poetry.lock b/poetry.lock index 096580a5f..7637723d7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -507,27 +507,6 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -[[package]] -name = "importlib-metadata" -version = "7.0.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version == \"3.9\"" -files = [ - {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, - {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf (>=0.9.2)", "pytest-ruff"] - [[package]] name = "jinja2" version = "3.1.3" @@ -882,7 +861,6 @@ files = [ [package.dependencies] altgraph = "*" -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} packaging = ">=22.0" pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""} @@ -907,7 +885,6 @@ files = [ ] [package.dependencies] -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} packaging = ">=22.0" setuptools = ">=42.0.0" @@ -933,7 +910,7 @@ description = "Python bindings for the Qt cross-platform application and UI fram optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <3.11" groups = ["main"] -markers = "python_version < \"3.11\" and extra == \"qt\"" +markers = "python_version == \"3.10\" and extra == \"qt\"" files = [ {file = "PySide2-5.15.2.1-5.15.2-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:b5e1d92f26b0bbaefff67727ccbb2e1b577f2c0164b349b3d6e80febb4c5bde2"}, {file = "PySide2-5.15.2.1-5.15.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:235240b6ec8206d9fdf0232472c6ef3241783d480425e5b54796f06e39ed23da"}, @@ -1017,7 +994,7 @@ description = "Python / C++ bindings helper module" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <3.11" groups = ["main"] -markers = "python_version < \"3.11\" and extra == \"qt\"" +markers = "python_version == \"3.10\" and extra == \"qt\"" files = [ {file = "shiboken2-5.15.2.1-5.15.2-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:f890f5611ab8f48b88cfecb716da2ac55aef99e2923198cefcf781842888ea65"}, {file = "shiboken2-5.15.2.1-5.15.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87079c07587859a525b9800d60b1be971338ce9b371d6ead81f15ee5a46d448b"}, @@ -1069,7 +1046,6 @@ babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.21" imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.13" @@ -1252,7 +1228,7 @@ description = "A lil' TOML parser" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -1288,27 +1264,10 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "zipp" -version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version == \"3.9\"" -files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-ruff"] - [extras] qt = ["pyside2"] [metadata] lock-version = "2.1" -python-versions = "^3.9,<3.13" -content-hash = "7409ca07c43208b622b22c5ae02b79cf9383c08c06302748c8d5c6ecbcf6f31c" +python-versions = "^3.10,<3.13" +content-hash = "304d5c9033aed625eb1f8cbbc2f23019fb708171bec6852c28ea8626221c0df0" diff --git a/pyproject.toml b/pyproject.toml index bfae95f03..6c465eb92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.9,<3.13" +python = "^3.10,<3.13" hidapi = ">=0.14.0" ecdsa = "~0" pyaes = "^1.6" diff --git a/setup.py b/setup.py index 0761871c8..12c5890c9 100644 --- a/setup.py +++ b/setup.py @@ -60,7 +60,7 @@ 'install_requires': install_requires, 'extras_require': extras_require, 'entry_points': entry_points, - 'python_requires': '>=3.9,<3.13', + 'python_requires': '>=3.10,<3.13', } diff --git a/test/setup_environment.sh b/test/setup_environment.sh index 7d602a5f1..1e71d197a 100755 --- a/test/setup_environment.sh +++ b/test/setup_environment.sh @@ -69,7 +69,7 @@ TREZOR_VERSION="core/v2.9.6" BITBOX01_VERSION="v7.1.0" BITBOX02_VERSION="firmware/v9.24.0" KEEPKEY_VERSION="v7.10.0" -SPECULOS_VERSION="v0.25.10" # Last version supporting Python 3.9 (v0.25.11+ requires >=3.10) +SPECULOS_VERSION="v0.25.10" # v0.25.11+ requires Python >=3.10 JADE_VERSION="1.0.36" # Keep COLDCARD_VERSION in sync with .github/actions/install-sim/action.yml From a608ea31356522626dd561a9152936f9167932e8 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 21 Jan 2026 17:34:36 +0100 Subject: [PATCH 7/9] Bump Speculos and Ledger Bitcoin app --- .github/workflows/ledger-app-builder.yml | 6 +++--- test/setup_environment.sh | 14 ++++++++++++-- test/test_ledger.py | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ledger-app-builder.yml b/.github/workflows/ledger-app-builder.yml index d873a2411..0d275679e 100644 --- a/.github/workflows/ledger-app-builder.yml +++ b/.github/workflows/ledger-app-builder.yml @@ -11,12 +11,12 @@ jobs: build: name: Build Bitcoin App runs-on: ${{ inputs.runs-on }} - # Pin to 4.23.0 for SDK v25.9.0 compatibility with Speculos v0.25.10 + # Pin to 4.23.0 for SDK v25.9.0 compatibility with Speculos v0.25.13 container: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:4.23.0 steps: - run: | - # Pin to v2.4.1 - last version that worked with HWI CI (PR #795 merged Sept 2025) - git clone --branch 2.4.1 --depth 1 https://github.com/LedgerHQ/app-bitcoin-new.git + # Pin to v2.4.2 + git clone --branch 2.4.2 --depth 1 https://github.com/LedgerHQ/app-bitcoin-new.git cd app-bitcoin-new make DEBUG=1 BOLOS_SDK=$NANOX_SDK - uses: actions/upload-artifact@v4 diff --git a/test/setup_environment.sh b/test/setup_environment.sh index 1e71d197a..b0350fe1c 100755 --- a/test/setup_environment.sh +++ b/test/setup_environment.sh @@ -69,7 +69,7 @@ TREZOR_VERSION="core/v2.9.6" BITBOX01_VERSION="v7.1.0" BITBOX02_VERSION="firmware/v9.24.0" KEEPKEY_VERSION="v7.10.0" -SPECULOS_VERSION="v0.25.10" # v0.25.11+ requires Python >=3.10 +SPECULOS_VERSION="v0.25.13" # Requires Python >=3.10 (v0.25.11+) JADE_VERSION="1.0.36" # Keep COLDCARD_VERSION in sync with .github/actions/install-sim/action.yml @@ -292,9 +292,19 @@ if [[ -n ${build_ledger} ]]; then cd speculos + # Work around -Werror build failures in Speculos' bundled deps. + # GCC < 15 errors out on unknown "-Wno-error=..." options, so only add the + # unterminated-string-initialization suppression when the compiler supports it. + CFLAGS="-O -fno-builtin -fPIC -Wall -Wextra -Werror -Wno-error=maybe-uninitialized -Wno-error=array-parameter -Wno-error=array-bounds" + cc_major=$(cc -dumpversion | cut -d. -f1) + if [ "${cc_major:-0}" -ge 15 ]; then + CFLAGS="$CFLAGS -Wno-error=unterminated-string-initialization" + fi + export CFLAGS + # Build the simulator. This is cached, but it is also fast mkdir -p build - cmake -Bbuild -S . + cmake -Bbuild -S . -DCMAKE_C_FLAGS="${CFLAGS}" make -C build/ cd .. diff --git a/test/test_ledger.py b/test/test_ledger.py index 6ab162e74..375ed39de 100755 --- a/test/test_ledger.py +++ b/test/test_ledger.py @@ -52,7 +52,7 @@ def start(self): super().start() automation_path = os.path.abspath("data/speculos-automation.json") app_path = f"./apps/btc-test{'-legacy' if self.legacy else ''}.elf" - os.environ["SPECULOS_APPNAME"] = "Bitcoin Test:1.6.6" if self.legacy else "Bitcoin Test:2.4.1" + os.environ["SPECULOS_APPNAME"] = "Bitcoin Test:1.6.6" if self.legacy else "Bitcoin Test:2.4.2" self.emulator_stderr = open('ledger-emulator.stderr', 'a') # Start the emulator From 2731a3f3405b18002564162299e817de4afda4bb Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 5 Sep 2025 12:37:35 +0200 Subject: [PATCH 8/9] test: reenable LedgerX tests --- test/test_device.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/test/test_device.py b/test/test_device.py index d22fdc386..ed656a297 100644 --- a/test/test_device.py +++ b/test/test_device.py @@ -591,20 +591,12 @@ def test_signtx(self): # https://github.com/bitcoin-core/HWI/pull/795#issuecomment-3112271927 raise unittest.SkipTest("Coldcard sign test temporarily disabled") - if self.emulator.type == "ledger" and not self.emulator.legacy: - # https://github.com/bitcoin-core/HWI/pull/795#issuecomment-3112271927 - raise unittest.SkipTest("Test temporarily disabled for NanoX") - for addrtypes, multisig_types, external, op_return in self.signtx_cases: with self.subTest(addrtypes=addrtypes, multisig_types=multisig_types, external=external, op_return=op_return): self._test_signtx(addrtypes, multisig_types, external, op_return) # Make a huge transaction which might cause some problems with different interfaces def test_big_tx(self): - if self.emulator.type == "ledger" and not self.emulator.legacy: - # https://github.com/bitcoin-core/HWI/pull/795#issuecomment-3112271927 - raise unittest.SkipTest("Test temporarily disabled for NanoX") - # make a huge transaction keypool_desc = self.do_command(self.dev_args + ["getkeypool", "--account", "10", "--addr-type", "sh_wit", "0", "100"]) self.assertIsInstance(keypool_desc, list, f"getkeypool returned error: {keypool_desc}") @@ -636,10 +628,6 @@ def test_big_tx(self): class TestDisplayAddress(DeviceTestCase): def test_display_address_path(self): - if self.emulator.type == "ledger" and not self.emulator.legacy: - # https://github.com/bitcoin-core/HWI/pull/795#issuecomment-3112271927 - raise unittest.SkipTest("Test temporarily disabled for NanoX") - result = self.do_command(self.dev_args + ['displayaddress', "--addr-type", "legacy", '--path', 'm/44h/1h/0h/0/0']) if self.emulator.supports_legacy: self.assertNotIn('error', result) @@ -665,10 +653,6 @@ def test_display_address_bad_path(self): self.assertEqual(result['code'], -7) def test_display_address_descriptor(self): - if self.emulator.type == "ledger" and not self.emulator.legacy: - # https://github.com/bitcoin-core/HWI/pull/795#issuecomment-3112271927 - raise unittest.SkipTest("Test temporarily disabled for NanoX") - account_xpub = self.do_command(self.dev_args + ['getxpub', 'm/84h/1h/0h'])['xpub'] p2sh_segwit_account_xpub = self.do_command(self.dev_args + ['getxpub', 'm/49h/1h/0h'])['xpub'] legacy_account_xpub = self.do_command(self.dev_args + ['getxpub', 'm/44h/1h/0h'])['xpub'] @@ -790,10 +774,6 @@ def _check_sign_msg(self, msg): self.assertTrue(self.rpc.verifymessage(addr, sig, msg)) def test_sign_msg(self): - if self.emulator.type == "ledger" and not self.emulator.legacy: - # https://github.com/bitcoin-core/HWI/pull/795#issuecomment-3112271927 - raise unittest.SkipTest("Test temporarily disabled for NanoX") - self._check_sign_msg("Message signing test") self._check_sign_msg("285") # Specific test case for Ledger shorter S From 363d3e3a0f02da796dba6487efc0966a2d3999de Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 30 Jan 2026 13:02:22 +0100 Subject: [PATCH 9/9] tmp: disable other devices, only python 3.10 --- .github/workflows/ci.yml | 94 ------------------------------- .github/workflows/device-test.yml | 2 +- 2 files changed, 1 insertion(+), 95 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10e2c5153..f4e88f2ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,42 +107,6 @@ jobs: name: Prepare sim matrices uses: ./.github/workflows/prepare-sim-matrices.yml - sim-builder-trezor: - name: Trezor sim builder - needs: prepare-sim-matrices - uses: ./.github/workflows/sim-builder.yml - with: - sim: trezor - include: ${{ needs.prepare-sim-matrices.outputs.trezor }} - runs-on: ubuntu-latest - - sim-builder-coldcard: - name: Coldcard sim builder - needs: prepare-sim-matrices - uses: ./.github/workflows/sim-builder.yml - with: - sim: coldcard - include: ${{ needs.prepare-sim-matrices.outputs.coldcard }} - runs-on: ubuntu-latest - - sim-builder-bitbox: - name: Bitbox sim builder - needs: prepare-sim-matrices - uses: ./.github/workflows/sim-builder.yml - with: - sim: bitbox - include: ${{ needs.prepare-sim-matrices.outputs.bitbox }} - runs-on: ubuntu-latest - - sim-builder-jade: - name: Jade sim builder - needs: prepare-sim-matrices - uses: ./.github/workflows/sim-builder.yml - with: - sim: jade - include: ${{ needs.prepare-sim-matrices.outputs.jade }} - runs-on: ubuntu-latest - sim-builder-ledger: name: Ledger sim builder needs: prepare-sim-matrices @@ -152,15 +116,6 @@ jobs: include: ${{ needs.prepare-sim-matrices.outputs.ledger }} runs-on: ubuntu-latest - sim-builder-keepkey: - name: Keepkey sim builder - needs: prepare-sim-matrices - uses: ./.github/workflows/sim-builder.yml - with: - sim: keepkey - include: ${{ needs.prepare-sim-matrices.outputs.keepkey }} - runs-on: ubuntu-latest - ledger-legacy-app-builder: name: Ledger Bitcoin Legacy App builder uses: ./.github/workflows/ledger-legacy-app-builder.yml @@ -180,20 +135,6 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/build-bitcoind - test-trezor-1: - uses: ./.github/workflows/device-test.yml - needs: [sim-builder-trezor, bitcoind-builder, dist-builder] - with: - device: trezor-1 - runs-on: ubuntu-latest - - test-trezor-t: - uses: ./.github/workflows/device-test.yml - needs: [sim-builder-trezor, bitcoind-builder, dist-builder] - with: - device: trezor-t - runs-on: ubuntu-latest - test-ledger-legacy: uses: ./.github/workflows/device-test.yml needs: [sim-builder-ledger, ledger-legacy-app-builder, bitcoind-builder, dist-builder] @@ -208,38 +149,3 @@ jobs: device: ledger runs-on: ubuntu-latest - test-coldcard: - uses: ./.github/workflows/device-test.yml - needs: [sim-builder-coldcard, bitcoind-builder, dist-builder] - with: - device: coldcard - runs-on: ubuntu-latest - - test-bitbox01: - uses: ./.github/workflows/device-test.yml - needs: [sim-builder-bitbox, bitcoind-builder, dist-builder] - with: - device: bitbox01 - runs-on: ubuntu-latest - - test-bitbox02: - uses: ./.github/workflows/device-test.yml - needs: [sim-builder-bitbox, bitcoind-builder, dist-builder] - with: - device: bitbox02 - runs-on: ubuntu-latest - - test-jade: - uses: ./.github/workflows/device-test.yml - needs: [sim-builder-jade, bitcoind-builder, dist-builder] - with: - device: jade - runs-on: ubuntu-latest - - test-keepkey: - uses: ./.github/workflows/device-test.yml - needs: [sim-builder-keepkey, bitcoind-builder, dist-builder] - with: - device: keepkey - runs-on: ubuntu-latest - diff --git a/.github/workflows/device-test.yml b/.github/workflows/device-test.yml index 5ec9ede76..fcf1d84d9 100644 --- a/.github/workflows/device-test.yml +++ b/.github/workflows/device-test.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.10', '3.11', '3.12'] + python-version: ['3.10'] device: - ${{ inputs.device }} test: