Skip to content

Make secp256k1 backend pluggable (coincurve/wallycore/pure-python) - #746

Merged
3rdIteration merged 22 commits into
masterfrom
feat/pluggable-secp256k1-backends
Jul 17, 2026
Merged

Make secp256k1 backend pluggable (coincurve/wallycore/pure-python)#746
3rdIteration merged 22 commits into
masterfrom
feat/pluggable-secp256k1-backends

Conversation

@3rdIteration

Copy link
Copy Markdown
Owner

Summary

Replace the hard coincurve dependency with a pluggable secp256k1 backend so BTCRecover works in environments without pre-built C-extension wheels (e.g. Termux, unusual platforms).

  • Add btcrecover/crypto_backends.py with automatic backend selection:
    1. coincurve (default when installed)
    2. wallycore (used when coincurve is absent; already a requirements-full dep)
    3. pure-Python fallback (bundled lib/ecpy) — prints a startup warning
  • New BTCR_BACKEND env var forces a backend (coincurve / wallycore / purepython), falling back if unavailable.
  • Route EC operations through the backend in btcrseed.py, btcrpass.py (incl. Electrum 2.8 ECIES + pickling) and lib/p2tr_helper/P2TR_tools.py.
  • requirements.txt: coincurve is now optional; wallycore>=1.0.0 added.
  • Add benchmark_crypto_backends.py comparing pubkey derivation, P2TR tweak, and ECIES multiply across backends.
  • Linux CI now exercises all three backends (Latest-* base+full, Weekly-Base, termux-tests).

Performance

For a standard BIP-39 recovery (public-key derivation dominated), the C backends are ~150× faster than pure-Python (~34–36k ops/s vs ~240 ops/s). Electrum 2.8 ECIES is only accelerated by coincurve (~48k ops/s); wallycore and pure-Python fall back to the bundled pure-Python EC code (~2k ops/s).

Test plan

  • Verified coincurve, wallycore-only, and pure-Python-only venvs each pass the relevant suites (P2TR seed recovery confirmed end-to-end under pure-Python).
  • Pre-existing failures on pristine HEAD (msigna, 3 walletfinder arg-parsing cases) are unrelated to this change.

🤖 Generated with opencode

- Add btcrecover/crypto_backends.py with auto-selection of coincurve,
  wallycore, or a bundled pure-Python fallback (with a startup warning)
- Support BTCR_BACKEND env override to force a backend
- Route EC operations through the backend in btcrseed, btcrpass
  (incl. Electrum 2.8 ECIES) and P2TR_tools
- requirements.txt: coincurve optional, wallycore>=1.0.0 added
- Add benchmark_crypto_backends.py
- Linux CI now tests all three backends (Latest/Weekly base+full, Termux)
- Document the three backends and relative performance in INSTALL.md
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-07-17 12:44 UTC

3rdIteration and others added 21 commits July 16, 2026 10:43
- coincurve install now installs requirements minus coincurve first (pulling in
  wallycore as fallback) and only then attempts coincurve, tolerating build
  failures (e.g. Python 3.14) instead of hard-failing the install step
- Drop wallycore from the Termux matrix: it has no Android/aarch64 wheel and its
  source build fails under Termux, so only coincurve and pure-python are tested there
…lback

- Verify via PyPI: neither coincurve 20.0.0 (wheels cp38-cp312) nor 21.0.0
  (wheels cp39-cp313) ships a Python 3.14 wheel, and both fail to build from
  source on 3.14. So coincurve simply cannot be installed on 3.14 today.
- Keep the CI coincurve install as 'coincurve==21.0.0 || coincurve==20.0.0 || true'
  (gets a wheel on 3.10-3.13) with '|| true' so 3.14 falls back to the already
  installed wallycore backend instead of failing the install step
- Update docs/INSTALL.md and skills/install-btcrecover/windows/SKILL.md: the
  previous claim that 'coincurve==20.0.0 has a 3.14 wheel' is incorrect; on 3.14
  users should install wallycore (or rely on the pure-Python fallback)
- 3.13 -> coincurve==21.0.0 (only version with a 3.13 wheel)
- 3.10/3.11/3.12 -> coincurve==21.0.0 || 20.0.0 (both have wheels)
- 3.14 -> no coincurve wheel; install allowed to fail and the job runs on the
  already-installed wallycore fallback backend
Use PEP 508 python_version markers so pip installs the coincurve release that
actually has a prebuilt wheel for the running interpreter:
  - python_version < 3.13  -> coincurve==20.0.0 (wheels cp38-cp312)
  - python_version >= 3.13 and < 3.14 -> coincurve==21.0.0 (cp313 wheel)
  - python_version >= 3.14 -> omitted (no coincurve wheel exists); BTCRecover
    falls back to the wallycore backend instead

This replaces the previous platform_machine-based split, which is no longer
needed since both releases now ship aarch64 and x86_64 wheels. Updated in
requirements.txt, requirements-full.txt, and the commented example in
requirements-walletfinder.txt.
… only

- test_walletfinder.py: the 3 argument-parsing tests referenced old argparse
  attribute names (args.wallet_mode / args.text_mode) that no longer exist after
  the backward-compat flags were renamed to *_compat. Update them to the current
  API (skip_wallet_mode default, text_mode_compat, wallet_mode_compat). These 3
  errors were failing every Linux/Windows/macOS test run (pre-existing on master).
- termux-tests.yml: on Android/aarch64 neither coincurve nor wallycore has a wheel
  or builds from source (and bip-utils forces coincurve), so Termux can only run
  the bundled pure-Python backend. Test that backend only, excluding the
  unbuildable C libraries from the install.
Termux/aarch64 cannot use coincurve (ofek/coincurve#189: build/runtime
incompatibility), so restore the original single-backend Termux jobs. Exclude
wallycore from the main install so only coincurve is attempted (matching the
historical Termux setup), avoiding the wallycore-from-source failure introduced
by adding wallycore to the base requirements.
…emp file

On Windows, 'pip install -r /dev/stdin' fails with
'Could not open requirements file: /proc/self/fd/0' (no /proc). Write the
filtered requirements to $RUNNER_TEMP/btcr-reqs.txt and install from that file
instead. This makes the coincurve install step (which previously failed on all
Windows Python versions) work, falling back to the wallycore wheel on 3.14.
The YAML block scalar preserved the backslash-escaped quotes as literal
characters, so the shell received a path wrapped in literal backslashes and the
temp requirements file was never created. Use plain double quotes.
bip-utils (and py_crypto_hd_wallet, slip10, stellar_sdk) hard-require
coincurve, which has no wheel and cannot build on Python 3.14. Exclude that
subtree from the Full requirements install on 3.14 so the job installs using
the wallycore fallback instead of failing on a coincurve source build.
…ch64)

Mirrors the Weekly/Latest Full fix: bip-utils and its dependents hard-require
coincurve, which has no aarch64 wheel and fails to build on Termux, so exclude
that subtree from the Termux Full install.
- btcrecover/aes_backends.py: Unified AES & ChaCha20-Poly1305 backend
  (pycryptodome -> pure-python fallback)
- lib/aes_gcm.py: Pure-python AES-GCM using pyaes + GHASH
- lib/chacha20_poly1305.py: Pure-python ChaCha20 + Poly1305 (RFC 8439)
- lib/keccak.py: Pure-python Keccak-256 (Ethereum-compatible)
- lib/eth_hash/backends/purepython.py: eth_hash backend wrapping lib.keccak
- btcrpass.py: Use aes_backends instead of direct Crypto.Cipher imports;
  improve protobuf error handling (exit -> ValueError/False)
- emip3.py: Use chacha20_poly1305_new from aes_backends
- requirements.txt: Document optional dependencies
- CI: Exclude pycryptodome/protobuf in purepython backend scenario so
  the pure-python fallbacks are actually exercised
load_aes256_library() returns _AESModule (the pure-python fallback),
but callers access __name__ on the result to estimate password speed.
Without __name__, the CI pure-python scenario crashed with:
  AttributeError: '_AESModule' object has no attribute '__name__'
The old 5-limb carry-chain decomposition was broken — when h values
exceeded 32 bits, it produced integers too large for struct.pack('<I', ...),
causing OverflowError in the pure-python backend CI scenario.

Rewrite _poly1305_mac using clean big-integer arithmetic (int.from_bytes /
to_bytes) that is provably correct and matches RFC 8439 test vectors.

Also fix a minor struct.pack call in the section about patating the other.
Bug found by CI testing with pycryptodome excluded.
- _inc32 now increments only the low 32 bits (GCM spec), preventing
  values >128 bits that caused OverflowError: int too big to convert
- _compute_j0 handles arbitrary-length IVs via GHASH (SP 800-38D),
  fixing Metamask/btc_com wallets that use 16-byte IVs
Sets BTCR_BACKEND env var for all subprocesses so users can compare
coincurve, wallycore, and pure-python performance from a single tool.
@3rdIteration
3rdIteration merged commit 1457088 into master Jul 17, 2026
39 checks passed
@3rdIteration
3rdIteration deleted the feat/pluggable-secp256k1-backends branch July 17, 2026 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant