Skip to content

Upgrade trezorlib to 0.20#10465

Open
romanz wants to merge 2 commits intospesmilo:masterfrom
romanz:trezorlib-0.20
Open

Upgrade trezorlib to 0.20#10465
romanz wants to merge 2 commits intospesmilo:masterfrom
romanz:trezorlib-0.20

Conversation

@romanz
Copy link
Copy Markdown
Contributor

@romanz romanz commented Feb 5, 2026

This PR updates Trezor plugin to use trezorlib 0.20 (trezor/trezor-firmware@45ff241).

Tested the following scenarios with Trezor 1 (with FW 1.14.0), T, Safe 3 and Safe 5 (with FW 2.10.0):

  • create a new wallet:
    • generate new seed
    • recover from backup
  • verify an address
  • send & RBF flows
  • set a PIN & a passphrase
  • open an existing wallet file
  • locking the device on exit
  • flow cancellation

Safe 7 support will be added in a subsequent PR.

@romanz romanz force-pushed the trezorlib-0.20 branch 2 times, most recently from f771198 to 76622d2 Compare February 5, 2026 19:56
Trezor plugin is updated for the upcoming `trezorlib` 0.20 release.

Tested the following scenarios with Trezor 1 (with FW 1.14.0), T, Safe 3 and Safe 5 (with FW 2.10.0):
- create a new wallet:
  - generate new seed
  - recover from backup
- verify an address
- send & RBF flows
- set a PIN & a passphrase
- open an existing wallet file
- locking the device on exit
- flow cancellation

Safe 7 support will be added in a subsequent PR.
@SomberNight
Copy link
Copy Markdown
Member

Thanks a lot for this!

I see you are bumping the minimum supported trezorlib version here and the new minimum is not tagged/released yet. So I guess this should not be merged as-is?

Alternatively, should we perhaps have some compatibility code to support both older and newer trezorlib?
(but normally I would agree that it is not worth the extra complexity to support older versions)

@romanz
Copy link
Copy Markdown
Contributor Author

romanz commented Feb 9, 2026

I see you are bumping the minimum supported trezorlib version here and the new minimum is not tagged/released yet. So I guess this should not be merged as-is?

We are planning to release trezorlib soon (hopefully this week) - so indeed it's better not to merge it yet.
However, it should be fine to review and test using a recent trezorlib, e.g. trezor/trezor-firmware@9d0a49a.

@romanz
Copy link
Copy Markdown
Contributor Author

romanz commented Feb 11, 2026

I see you are bumping the minimum supported trezorlib version here and the new minimum is not tagged/released yet. So I guess this should not be merged as-is?

We are planning to release trezorlib soon (hopefully this week) - so indeed it's better not to merge it yet. However, it should be fine to review and test using a recent trezorlib, e.g. trezor/trezor-firmware@9d0a49a.

trezorlib 0.20 has been tagged and released to PyPI :)

@SomberNight
Copy link
Copy Markdown
Member

SomberNight commented Feb 11, 2026

trezorlib==0.20.0 introduced several new required runtime and build-time dependencies. Are all of these really needed?

For example, can keyring be made optional? Is it needed for our usecase? And platformdirs?
Is hatchling really needed for building? :/ So far we managed to avoid pulling it in, it has too many transitive dependencies :(


My notes follow:

Added 8 new runtime dependencies, 2 runtime dependencies removed. As for build-time, because of needing hatchling, 5 new build-time dependencies are pulled in, and hatchling itself we cannot really build from source.


Runtime:

removed: ecdsa (and six)
added: keyring (and SecretStorage, jeepney, jaraco.classes, jaraco.functools, more-itertools, jaraco.context), noiseprotocol (but bitbox02 already pulls that in anyway), platformdirs

Runtime old deps
$ pipdeptree
pipdeptree==2.30.0
├── packaging [required: >=25, installed: 26.0]
└── pip [required: >=25.2, installed: 26.0.1]
setuptools==82.0.0
trezor==0.13.10
├── ecdsa [required: >=0.9, installed: 0.19.1]
│   └── six [required: >=1.9.0, installed: 1.17.0]
├── mnemonic [required: >=0.20, installed: 0.21]
├── shamir-mnemonic [required: >=0.3.0, installed: 0.3.0]
├── slip10 [required: >=1.0.1, installed: 1.1.0]
│   └── cryptography [required: Any, installed: 46.0.5]
│       └── cffi [required: >=2.0.0, installed: 2.0.0]
│           └── pycparser [required: Any, installed: 3.0]
├── requests [required: >=2.4.0, installed: 2.32.5]
│   ├── charset-normalizer [required: >=2,<4, installed: 3.4.4]
│   ├── idna [required: >=2.5,<4, installed: 3.11]
│   ├── urllib3 [required: >=1.21.1,<3, installed: 2.6.3]
│   └── certifi [required: >=2017.4.17, installed: 2026.1.4]
├── click [required: >=7,<8.2, installed: 8.1.8]
├── libusb1 [required: >=1.6.4, installed: 3.3.1]
├── construct [required: >=2.9,!=2.10.55, installed: 2.10.70]
├── typing_extensions [required: >=4.7.1, installed: 4.15.0]
├── construct-classes [required: >=0.1.2, installed: 0.2.2]
│   └── construct [required: ~=2.10, installed: 2.10.70]
└── cryptography [required: >=41, installed: 46.0.5]
    └── cffi [required: >=2.0.0, installed: 2.0.0]
        └── pycparser [required: Any, installed: 3.0]
wheel==0.46.3
└── packaging [required: >=24.0, installed: 26.0]
Runtime new deps
$ pipdeptree
pipdeptree==2.30.0
├── packaging [required: >=25, installed: 26.0]
└── pip [required: >=25.2, installed: 26.0.1]
setuptools==82.0.0
trezor==0.20.0
├── click [required: >=8,<9, installed: 8.3.1]
├── construct [required: >=2.9,!=2.10.55, installed: 2.10.70]
├── construct-classes [required: >=0.1.2, installed: 0.2.2]
│   └── construct [required: ~=2.10, installed: 2.10.70]
├── cryptography [required: >=41, installed: 46.0.5]
│   └── cffi [required: >=2.0.0, installed: 2.0.0]
│       └── pycparser [required: Any, installed: 3.0]
├── keyring [required: >=25.7.0, installed: 25.7.0]
│   ├── SecretStorage [required: >=3.2, installed: 3.5.0]
│   │   ├── cryptography [required: >=2.0, installed: 46.0.5]
│   │   │   └── cffi [required: >=2.0.0, installed: 2.0.0]
│   │   │       └── pycparser [required: Any, installed: 3.0]
│   │   └── jeepney [required: >=0.6, installed: 0.9.0]
│   ├── jeepney [required: >=0.4.2, installed: 0.9.0]
│   ├── jaraco.classes [required: Any, installed: 3.4.0]
│   │   └── more-itertools [required: Any, installed: 10.8.0]
│   ├── jaraco.functools [required: Any, installed: 4.4.0]
│   │   └── more-itertools [required: Any, installed: 10.8.0]
│   └── jaraco.context [required: Any, installed: 6.1.0]
├── libusb1 [required: >=1.6.4, installed: 3.3.1]
├── mnemonic [required: >=0.20, installed: 0.21]
├── noiseprotocol [required: >=0.3.1,<0.4.0, installed: 0.3.1]
│   └── cryptography [required: >=2.8, installed: 46.0.5]
│       └── cffi [required: >=2.0.0, installed: 2.0.0]
│           └── pycparser [required: Any, installed: 3.0]
├── platformdirs [required: >=4.4.0, installed: 4.5.1]
├── requests [required: >=2.4.0, installed: 2.32.5]
│   ├── charset-normalizer [required: >=2,<4, installed: 3.4.4]
│   ├── idna [required: >=2.5,<4, installed: 3.11]
│   ├── urllib3 [required: >=1.21.1,<3, installed: 2.6.3]
│   └── certifi [required: >=2017.4.17, installed: 2026.1.4]
├── shamir-mnemonic [required: >=0.3.0, installed: 0.3.0]
├── slip10 [required: >=1.0.1, installed: 1.1.0]
│   └── cryptography [required: Any, installed: 46.0.5]
│       └── cffi [required: >=2.0.0, installed: 2.0.0]
│           └── pycparser [required: Any, installed: 3.0]
└── typing_extensions [required: >=4.7.1, installed: 4.15.0]
wheel==0.46.3
└── packaging [required: >=24.0, installed: 26.0]

build-time:

How to install old trezorlib
/opt/cpython-versions/3.10.19/bin/python3 -m venv env3
source env3/bin/activate

pip install --no-cache-dir --no-build-isolation --no-dependencies -r ~/wspace/electrum/contrib/deterministic-build/requirements-build-base.txt
pip install --no-cache-dir --no-build-isolation --no-dependencies --no-binary :all: -r ~/wspace/electrum/contrib/deterministic-build/requirements-build-appimage.txt

pip install --no-cache-dir --no-build-isolation --no-dependencies --no-binary :all: "trezor==0.13.10"
How to install new trezorlib
/opt/cpython-versions/3.10.19/bin/python3 -m venv env3
source env3/bin/activate

pip install --no-cache-dir --no-build-isolation --no-dependencies -r ~/wspace/electrum/contrib/deterministic-build/requirements-build-base.txt
pip install --no-cache-dir --no-build-isolation --no-dependencies --no-binary :all: -r ~/wspace/electrum/contrib/deterministic-build/requirements-build-appimage.txt


pip install --no-cache-dir --no-build-isolation --no-dependencies --no-binary :all: pathspec
pip install --no-cache-dir --no-build-isolation --no-dependencies --no-binary :all: pluggy
pip install --no-cache-dir --no-build-isolation --no-dependencies --no-binary :all: calver
pip install --no-cache-dir --no-build-isolation --no-dependencies --no-binary :all: trove-classifiers
pip install --no-cache-dir --no-build-isolation --no-dependencies --no-binary :all: hatchling

pip install --no-cache-dir --no-build-isolation --no-dependencies --no-binary :all: "trezor==0.20.0"

EDIT: it's even worse I made too many shortcuts for my build-time checks... e.g. platformdirs pulls in hatch-vcs, and construct-classes now pulls in uv_build and that pulls in Rust :(
Don't have time to fully investigate now, but the graph certainly became MUCH larger.

@SomberNight
Copy link
Copy Markdown
Member

and construct-classes now pulls in uv_build and that pulls in Rust :(

$ pip install --no-cache-dir --no-build-isolation --no-dependencies --no-binary :all: uv_build
Collecting uv_build
  Downloading uv_build-0.10.2.tar.gz (361 kB)
  Preparing metadata (pyproject.toml) ... done
Building wheels for collected packages: uv_build
  Building wheel for uv_build (pyproject.toml) ... error
  error: subprocess-exited-with-error
  
  × Building wheel for uv_build (pyproject.toml) did not run successfully.
  │ exit code: 1
  ╰─> [33 lines of output]
      Running `maturin pep517 build-wheel -i /home/user/wspace/tmp/env4/bin/python3 --compatibility off`
      📦 Including license file `LICENSE-APACHE`
      📦 Including license file `LICENSE-MIT`
      🍹 Building a mixed python/rust project
      🔗 Found bin bindings
      📡 Using build options bindings from pyproject.toml
      error: rustc 1.85.0 is not supported by the following packages:
        uv-build@0.10.2 requires rustc 1.91
        uv-build-backend@0.0.22 requires rustc 1.91
        uv-cache-key@0.0.22 requires rustc 1.91
        uv-distribution-filename@0.0.22 requires rustc 1.91
        uv-fs@0.0.22 requires rustc 1.91
        uv-git-types@0.0.22 requires rustc 1.91
        uv-globfilter@0.0.22 requires rustc 1.91
        uv-logging@0.0.22 requires rustc 1.91
        uv-macros@0.0.22 requires rustc 1.91
        uv-normalize@0.0.22 requires rustc 1.91
        uv-options-metadata@0.0.22 requires rustc 1.91
        uv-pep440@0.0.22 requires rustc 1.91
        uv-pep508@0.0.22 requires rustc 1.91
        uv-platform-tags@0.0.22 requires rustc 1.91
        uv-preview@0.0.22 requires rustc 1.91
        uv-pypi-types@0.0.22 requires rustc 1.91
        uv-redacted@0.0.22 requires rustc 1.91
        uv-small-str@0.0.22 requires rustc 1.91
        uv-static@0.0.22 requires rustc 1.91
        uv-version@0.10.2 requires rustc 1.91
        uv-warnings@0.0.22 requires rustc 1.91
      
      💥 maturin failed
        Caused by: Failed to build a native library through cargo
        Caused by: Cargo build finished with "exit status: 101": `env -u CARGO "cargo" "rustc" "--profile" "release" "--message-format" "json-render-diagnostics" "--manifest-path" "/tmp/pip-install-c6sg1ixk/uv-build_85e8fd5d9cbb4a5dac6a0d1ff550cd84/crates/uv-build/Cargo.toml" "--bin" "uv-build" "--" "-C" "strip=symbols"`
      Error: command ['maturin', 'pep517', 'build-wheel', '-i', '/home/user/wspace/tmp/env4/bin/python3', '--compatibility', 'off'] returned non-zero exit status 1
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for uv_build
Failed to build uv_build

[notice] A new release of pip is available: 25.1.1 -> 26.0.1
[notice] To update, run: pip install --upgrade pip
ERROR: Failed to build installable wheels for some pyproject.toml based projects (uv_build)

uv_build... Just great :)

@matejcik
Copy link
Copy Markdown
Contributor

it's stupid that the build engines are pulled in. neither trezorlib nor construct_classes actually care about what builds them, there is no "build process" beyond the old thing that distutils could do fine. this is all a consequence of locally using uv for managing dependencies, and that requires us to specify a build engine.

off the top of your head, do you know of a build engine that is compatible with pyproject.toml and doesn't impose burdens on you?
(AI tells me flit exists?)

platformdirs and keyring are only required for the trezorctl key management backend. we want to keep them on by default because we want trezorctl to be present by default ... but you don't need it and if you can, you could blacklist those. I think. (if something fails due to blacklisting them, i'd consider that a bug to solve in trezorlib)

going forward i think we'll have to split off a trezorlib package that will be lighter on dependencies, and a trezor package that pulls in the default subset for end-users. but that's gonna take some time.

@SomberNight
Copy link
Copy Markdown
Member

SomberNight commented Feb 12, 2026

it's stupid that the build engines are pulled in.

Right. I don't like it either.
However we don't want to download pre-compiled packages from PyPI but instead build everything practical from source ourselves. In practice this means we download sdists, instead of wheels. We pip install packages during our builds (e.g. AppImage, macOS dmg, ...), and at that time pip insists on creating wheels (locally, internally) from each sdist. It is that build step that pulls in setuptools/wheel/poetry-core/flit-core/hatchling/uv-build and basically EVERY build backend that ever existed and will ever exist. The ever-expanding fragmentation of the python ecosystem is really hurting our goal of minimising supply-chain trust here.

Now I realise source-only wheels are somewhat of a weird edge-case. I guess we could theoretically also accept those in addition to sdists, however it does not seem easily possible to instruct pip and the related tools to accept either sdists or source-only wheels. Also, I am not sure if it's possible to reliably tell if a wheel is actually source-only but I guess this is a conceptually separate issue - sdists too could incorrectly include compiled code inside. Still, only allowing sdists allows us to only review just a single distributable (the tar.gz) for each of our dependencies on PyPI. Even that is a herculean effort.

this is all a consequence of locally using uv for managing dependencies, and that requires us to specify a build engine.

That's the core of the issue. If we want to build from sdists in our build scripts, what PEP-517 build backend our dependencies choose we need to pull in. I understand that some of these build backends are very convenient for the developers of a given library (python package) during their normal workflow, but they actually put a burden on downstream projects who don't just want to blindly download binaries from PyPI. Perhaps the problem is our approach, perhaps we are doing it wrong, but to me it just seems like the whole python ecosystem just does not seem to care about reproducing and bootstrapping builds. People seem okay with downloading random binaries from PyPI and not to care being able to build them themselves, much less cross-compile them. Have you ever tried building a hidapi or a cryptography wheel on Linux for a Windows target? It's madness. We gave up on that (for now).

see here to get a grasp of how e.g. our AppImage build works:

info "Installing build dependencies."
# note: re pip installing from PyPI,
# we prefer compiling C extensions ourselves, instead of using binary wheels,
# hence "--no-binary :all:" flags. However, we specifically allow
# - PyQt6, as it's harder to build from source
# - cryptography, as it's harder to build from source
# - the whole of "requirements-build-base.txt", which includes pip and friends, as it also includes "wheel",
# and I am not quite sure how to break the circular dependence there (I guess we could introduce
# "requirements-build-base-base.txt" with just wheel in it...)
"$python" -m pip install --no-build-isolation --no-dependencies --no-warn-script-location \
--cache-dir "$PIP_CACHE_DIR" -r "$CONTRIB/deterministic-build/requirements-build-base.txt"
"$python" -m pip install --no-build-isolation --no-dependencies --no-binary :all: --no-warn-script-location \
--cache-dir "$PIP_CACHE_DIR" -r "$CONTRIB/deterministic-build/requirements-build-appimage.txt"
# opt out of compiling C extensions
export YARL_NO_EXTENSIONS=1
export FROZENLIST_NO_EXTENSIONS=1
export ELECTRUM_ECC_DONT_COMPILE=1
info "installing electrum and its dependencies."
"$python" -m pip install --no-build-isolation --no-dependencies --no-binary :all: --no-warn-script-location \
--cache-dir "$PIP_CACHE_DIR" -r "$CONTRIB/deterministic-build/requirements.txt"
"$python" -m pip install --no-build-isolation --no-dependencies --no-binary :all: --only-binary PyQt6,PyQt6-Qt6,cryptography --no-warn-script-location \
--cache-dir "$PIP_CACHE_DIR" -r "$CONTRIB/deterministic-build/requirements-binaries.txt"
"$python" -m pip install --no-build-isolation --no-dependencies --no-binary :all: --no-warn-script-location \
--cache-dir "$PIP_CACHE_DIR" -r "$CONTRIB/deterministic-build/requirements-hw.txt"
"$python" -m pip install --no-build-isolation --no-dependencies --no-warn-script-location \
--cache-dir "$PIP_CACHE_DIR" "$PROJECT_ROOT"
# was only needed during build time, not runtime
"$python" -m pip uninstall -y Cython

see here for requirements-build-base.txt and its generated counterpart

(Note how some packages such as PyQt6 and cryptography we begrudgingly give a lot more trust and just take their binary wheels. We have a few similar whitelists across the different builds that would be great to make shorter over time.)

off the top of your head, do you know of a build engine that is compatible with pyproject.toml and doesn't impose burdens on you?
(AI tells me flit exists?)

setuptools, flit-core, and poetry-core we already added to requirements-build-base, so those are already pulled in and trusted. flit-core and poetry-core are both ~minimal and don't have any transitive dependencies of their own. setuptools (and pip and wheel) is unavoidable and we consider it ultimately trusted due to historical reasons in any case.

platformdirs and keyring are only required for the trezorctl key management backend. we want to keep them on by default because we want trezorctl to be present by default ... but you don't need it and if you can, you could blacklist those.

Ok, right, that sounds good in theory. :) I will have a look at how to do the blacklisting in practice.

going forward i think we'll have to split off a trezorlib package that will be lighter on dependencies, and a trezor package that pulls in the default subset for end-users. but that's gonna take some time.

Yes, please. Something similar to the trezor-parts vendored inside HWI, a fully stripped-down bare minimum library to communicate with the hardware but that you guys maintain would be extremely desirable. For example, I would rather not pull in Click either :D

Alternatively, perhaps long-term we should run the hardware-device comms stuff in a separate process and just do IPC with the main python electrum process. That way we would be able to afford to care less about what dependencies hardware wallet support pulls in. Currently if a user even tries to scan for connected hardware signers, all the plugins get loaded and get to execute code, and then if the user later opens a software-seeded wallet without restarting the python process, their seed in memory is fair game...

@matejcik
Copy link
Copy Markdown
Contributor

btw I think you can pin construct_classes to 1.x to get rid of uv_build?

@SomberNight
Copy link
Copy Markdown
Member

btw I think you can pin construct_classes to 1.x to get rid of uv_build?

Yes, that looks correct. For now we can pin "construct_classes<0.2".

@SomberNight
Copy link
Copy Markdown
Member

Besides the new dependencies, this pull request looks simple and could go in. But I don't want to pull in new dependencies. In general I would like to ask you to be very conscious about the dependencies you pull in and avoid doing so whenever possible. Dependencies that are maintained by the same people are a special case, so e.g. construct-classes does not even really count (but uv-build would).

Is hatchling really needed for building? :/ So far we managed to avoid pulling it in, it has too many transitive dependencies

What about hatchling in the build system of trezorlib? Could you use setuptools/wheel/poetry-core/flit-core instead?
Can't you just use setuptools? That's what previous versions used. Sure you migrated to pyproject.toml now, but that's no excuse in itself for ditching setuptools.
See e.g. https://github.com/spesmilo/electrum-aionostr/blob/43a3965fd05c49dba510d4f406522ac2a530f192/pyproject.toml#L3

platformdirs and keyring are only required for the trezorctl key management backend. we want to keep them on by default because we want trezorctl to be present by default ... but you don't need it and if you can, you could blacklist those.

Ok, right, that sounds good in theory. :) I will have a look at how to do the blacklisting in practice.

This is in my backlog but haven't had time to look into it yet and might not for a while...
Help would be welcome, either on our side to make the build scripts more advanced, or on yours to make the new dependencies declared optional.

The method works as follows:
1. for every blacklisted dependency, as listed in ghost.txt,
   create and install an empty ghost package which will satisfy
   the dependency resolver
2. before hash resolution step, remove those ghosts to make hashin happy

This required converting find_restricted_dependencies to use locally
installed package metadata instead of looking it up online on pypi. But
that seems to be a good idea anyway.
@matejcik
Copy link
Copy Markdown
Contributor

matejcik commented Mar 6, 2026

What about hatchling in the build system of trezorlib? Could you use setuptools/wheel/poetry-core/flit-core instead?
Can't you just use setuptools? That's what previous versions used. Sure you migrated to pyproject.toml now, but that's no excuse in itself for ditching setuptools.

switching to flit in trezor/trezor-firmware#6564
setuptools is great but it's also a quagmire that i'd prefer to avoid in future projects; sometimes i feel like it is worth it to start over from scratch and lose all the accumulated and hard-won features. ditching setuptools was a pleasant side-effect of the switch to pyproject.toml

This is in my backlog but haven't had time to look into it yet and might not for a while...
Help would be welcome, either on our side to make the build scripts more advanced, or on yours to make the new dependencies declared optional.

have a look at 797f5c8 (i'm sure @romanz can cherry-pick it if you like it)

this is certainly a faster option than waiting for our side to split up trezorlib into multiple packages

@matejcik
Copy link
Copy Markdown
Contributor

matejcik commented Mar 6, 2026

general note: my first instinct (as a former distro package maintainer) would be to fetch the sdist and patch the pyproject.toml inside. that's what i wanted to do originally, because i thought your source-only build process is better than pip install --no-binary (which i didn't know existed)

that way you could get rid of uv_build wherever you see it :) it's still an option, though probably somewhat more involved because of how you use deterministic-build/requirements.txt

@SomberNight
Copy link
Copy Markdown
Member

SomberNight commented Mar 6, 2026

switching to flit in trezor/trezor-firmware#6564

Nice, thank you.

Help would be welcome, either on our side to make the build scripts more advanced, or on yours to make the new dependencies declared optional.

have a look at 797f5c8 (i'm sure @romanz can cherry-pick it if you like it)

Great, that looks promising! Please cherry-pick it to the branch.
Thanks for the effort, I appreciate it!

I tried to build from that commit - but ofc as it relies on trezor/trezor-firmware#6564, I had to hack some more stuff on top.
In the end, this commit worked: SomberNight@45274d5 (just for reference)

my first instinct (as a former distro package maintainer) would be to fetch the sdist and patch the pyproject.toml inside

Right, it sounds feasible but not sure how much work it would be to maintain the patches.
But indeed it is something we might have to do at some point...

@@ -153,8 +155,8 @@ class TrezorPlugin(HW_PluginBase):
libraries_URL = 'https://pypi.org/project/trezor/'
minimum_firmware = (1, 5, 2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tested this on a T1 with fw 1.12.1 and with a model T with fw 2.6.4. Worked ok.
I expect you tested cutting edge fw, so no point for me to repeat that.

I also have another T1 with fw 1.8.3. That device works with electrum master and trezorlib 0.13.x, but not with this branch and new trezorlib.

This is what I get in the Electrum Wizard when trying to create a wallet with that trezor model one:

 10.69 | D | gui.qt.wizard.wallet | resolve_next view is trezor_xpub
 10.69 | D | gui.qt.wizard.wallet | wizard stack:
0: 0x7f3e8cd72d00 - {}
1: 0x7f3e8cd88200 - {'wallet_name': 'wallet_88', 'wallet_exists': False, 'wallet_is_open': False, 'password': '<redacted>', 'wallet_needs_hw_unlock': False}
2: 0x7f3e8cd81d00 - {'wallet_name': 'wallet_88', 'wallet_exists': False, 'wallet_is_open': False, 'password': '<redacted>', 'wallet_needs_hw_unlock': False, 'wallet_type': 'standard'}
3: 0x7f3e771a9880 - {'wallet_name': 'wallet_88', 'wallet_exists': False, 'wallet_is_open': False, 'password': '<redacted>', 'wallet_needs_hw_unlock': False, 'wallet_type': 'standard', 'keystore_type': 'hardware'}
4: 0x7f3e77360a00 - {'wallet_name': 'wallet_88', 'wallet_exists': False, 'wallet_is_open': False, 'password': '<redacted>', 'wallet_needs_hw_unlock': False, 'wallet_type': 'standard', 'keystore_type': 'hardware', 'hardware_device': ('trezor', DeviceInfo(device=Device(path='webusb:001:4', interface_number=-1, id_='webusb:001:4', product_key='Trezor', usage_page=0, transport_ui_string='webusb:001:4'), label='white', initialized=True, exception=None, plugin_name='trezor', soft_device_id='466354651092540CD6071750', model_name='Trezor One'))}
c: 0x7f3e77360f80 - {'wallet_name': 'wallet_88', 'wallet_exists': False, 'wallet_is_open': False, 'password': '<redacted>', 'wallet_needs_hw_unlock': False, 'wallet_type': 'standard', 'keystore_type': 'hardware', 'hardware_device': ('trezor', DeviceInfo(device=Device(path='webusb:001:4', interface_number=-1, id_='webusb:001:4', product_key='Trezor', usage_page=0, transport_ui_string='webusb:001:4'), label='white', initialized=True, exception=None, plugin_name='trezor', soft_device_id='466354651092540CD6071750', model_name='Trezor One')), 'script_type': 'p2wpkh', 'derivation_path': 'm/84h/1h/0h'}
 10.70 | D | gui.qt.wizard.wallet | load_next_component: <class 'electrum.plugins.trezor.qt.WCTrezorXPub'>
 10.70 | D | gui.qt.wizard.wallet | view "trezor_xpub" last: False
 10.74 | E | trezorlib.protocol_v1 | Trezor did not return a session ID. Session management is now broken.
/tmp/.mount_electrdPnlnC/usr/lib/python3.12/site-packages/trezorlib/protocol_v1.py:210: UserWarning: Your Trezor firmware does not support root fingerprint.
  warnings.warn("Your Trezor firmware does not support root fingerprint.")
 11.25 | E | plugins.trezor.qt.WCTrezorXPub | AssertionError()
Traceback (most recent call last):
  File "/tmp/.mount_electrdPnlnC/usr/lib/python3.12/site-packages/electrum/gui/qt/wizard/wallet.py", line 1438, in get_xpub_task
    self.xpub = self.get_xpub_from_client(_client, _derivation, _xtype)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.mount_electrdPnlnC/usr/lib/python3.12/site-packages/electrum/plugins/trezor/qt.py", line 815, in get_xpub_from_client
    return client.get_xpub(derivation, xtype, True)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.mount_electrdPnlnC/usr/lib/python3.12/site-packages/electrum/plugin.py", line 993, in wrapper
    return run_in_hwd_thread(partial(func, *args, **kwargs))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.mount_electrdPnlnC/usr/lib/python3.12/site-packages/electrum/plugin.py", line 986, in run_in_hwd_thread
    return fut.result()
           ^^^^^^^^^^^^
  File "/tmp/.mount_electrdPnlnC/usr/lib/python3.12/concurrent/futures/_base.py", line 456, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/tmp/.mount_electrdPnlnC/usr/lib/python3.12/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/tmp/.mount_electrdPnlnC/usr/lib/python3.12/concurrent/futures/thread.py", line 59, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.mount_electrdPnlnC/usr/lib/python3.12/site-packages/electrum/plugins/trezor/clientbase.py", line 183, in get_xpub
    node = trezorlib.btc.get_public_node(self.session, address_n).node
                                         ^^^^^^^^^^^^
  File "/tmp/.mount_electrdPnlnC/usr/lib/python3.12/site-packages/electrum/plugins/trezor/clientbase.py", line 82, in session
    session.ensure_unlocked()
  File "/tmp/.mount_electrdPnlnC/usr/lib/python3.12/site-packages/trezorlib/tools.py", line 336, in wrapper
    return context_func(context, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.mount_electrdPnlnC/usr/lib/python3.12/site-packages/trezorlib/client.py", line 184, in ensure_unlocked
    assert resp.root_fingerprint is not None
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

I did not investigate the root cause. I guess if you think that's the best way to handle, we could just bump minimum_firmware. but you need to investigate at least to the degree that you know which version to bump to. :)

OTOH note that some people with geo-distributed cold multisigs likely won't be happy if we bump the minimum. Up to you.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Will take a look tomorrow.

Copy link
Copy Markdown
Contributor Author

@romanz romanz Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trezor/trezor-firmware#6580 should support old FWs.

keystore_class = TrezorKeyStore
minimum_library = (0, 13, 0)
maximum_library = (0, 14)
minimum_library = (0, 20, 0)
Copy link
Copy Markdown
Member

@SomberNight SomberNight Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please see SomberNight@f8226f9 and cherry-pick that or add something similar manually

I built an appimage and found that the plugin is broken at runtime due to the change to trezorlib.__version__.
ref https://github.com/trezor/trezor-firmware/blame/e754ba60a15c770de4667f072ae4e1f09c58e731/python/src/trezorlib/__init__.py#L21-L28
(btw note how the code says removal in 0.15 -- and you jumped from 0.14 to 0.20, guess that needs updating)

  8.16 | W | gui.qt.wizard.wallet.WCChooseHWDevice | error getting device infos for trezor: Library version for 'trezor' is incompatible. // Installed: unknown, Needed: 0.20.0 <= x < 0.21 // Make sure you install it with python3
>>> trezorlib.__version__
Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name 'trezorlib' is not defined
>>> import trezorlib
>>> trezorlib.__version__
Traceback (most recent call last):
  File "/tmp/.mount_electrcmMljC/usr/lib/python3.12/importlib/metadata/__init__.py", line 397, in from_name
    return next(cls.discover(name=name))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
StopIteration

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/tmp/.mount_electrcmMljC/usr/lib/python3.12/site-packages/trezorlib/__init__.py", line 27, in __getattr__
    return importlib.metadata.version("trezor")
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.mount_electrcmMljC/usr/lib/python3.12/importlib/metadata/__init__.py", line 889, in version
    return distribution(distribution_name).version
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.mount_electrcmMljC/usr/lib/python3.12/importlib/metadata/__init__.py", line 862, in distribution
    return Distribution.from_name(distribution_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.mount_electrcmMljC/usr/lib/python3.12/importlib/metadata/__init__.py", line 399, in from_name
    raise PackageNotFoundError(name)
importlib.metadata.PackageNotFoundError: No package metadata was found for trezor

We atm have a whitelist for which packages get .dist-info/ included and trezor needs to be put on that list now.
That's fine, it just needs to be added.

The issue in general with the .dist-info metadata stuff is that the RECORD file is often not reproducible, making the whole build not deterministic (see c52a29f). Usually it is due to packages building .so files with debug symbols, pip-random-generated-filesystem-paths, etc included. All that non-determinism in actual .so files we strip after-the-fact, see

info "stripping binaries from debug symbols."
# "-R .note.gnu.build-id" also strips the build id
# "-R .comment" also strips the GCC version information
strip_binaries()
{
chmod u+w -R "$APPDIR"
{
printf '%s\0' "$APPDIR/usr/bin/python${PY_VER_MAJOR}"
find "$APPDIR" -type f -regex '.*\.so\(\.[0-9.]+\)?$' -print0
} | xargs -0 --no-run-if-empty --verbose strip -R .note.gnu.build-id -R .comment
}
strip_binaries
, so the .so files we bundle are reproducible but the .dist-info/RECORD files are not. Further note that this stripping we do obviously changes the hashes of the .so files, making the RECORD files not consistent with the actual state. Hence it is best not to include the RECORD files (and we just rm the whole .dist-info/ dir).

but I just built two appimages and trezorlib's RECORD file is reproducible, so it can get whitelisted.

@romanz
Copy link
Copy Markdown
Contributor Author

romanz commented Mar 8, 2026

Great, that looks promising! Please cherry-pick it to the branch.

Cherry-picked as 797f5c8.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants