bitcurve
Fast libsecp256k1 bindings for Python with a coincurve compatible API.
- ECDSA (RFC6979) signing and verification, recoverable signatures, BIP340 Schnorr signatures, x-only keys, ECDH, key tweaking, DER/PEM private keys.
- Written in Rust with PyO3 on top of the
secp256k1crate, which bundles bitcoin-core's libsecp256k1. - Built against the stable Python ABI (abi3): one wheel per platform works on Python 3.10, 3.15 and every later version, there is nothing to rebuild when a new Python comes out.
- No runtime dependencies.
Why bitcurve
coincurve is the library most of the Python Bitcoin and Lightning ecosystem uses for secp256k1, including lnurl, bolt11 and LNbits. Its last release, 21.0.0, is from 2025-03-08, more than a year ago, and it is now breaking installs:
- coincurve 21.0.0 ships wheels only up to Python 3.13 and its source build fails against current cffi, so it can not be installed on Python 3.14 or 3.15.
- coincurve 20.0.0 no longer builds at all with scikit-build-core 1.0.
- The fixes for Python 3.14 are merged upstream (ofek/coincurve#225, 2026-08-08), but have not been released, see the requests in ofek/coincurve#219.
bitcurve exists so these projects keep working on every Python version without waiting for a coincurve release. Because the wheels use the stable ABI, the same problem can not happen again with the next Python release.
Install
pip install bitcurve
Usage
from bitcurve import PrivateKey, PublicKey, PublicKeyXOnly, verify_signature
key = PrivateKey()
signature = key.sign(b"hello")
assert key.public_key.verify(signature, b"hello")
assert verify_signature(signature, b"hello", key.public_key.format())
# recoverable signatures, as used by BOLT11 invoices
recoverable = key.sign_recoverable(b"hello")
assert PublicKey.from_signature_and_message(recoverable, b"hello") == key.public_key
# BIP340 Schnorr signatures
digest = bytes(32)
schnorr = key.sign_schnorr(digest)
assert key.public_key_xonly.verify(schnorr, digest)
assert PublicKeyXOnly(key.public_key_xonly.format()).verify(schnorr, digest)
Migrating from coincurve
bitcurve implements the public API of coincurve 21: PrivateKey, PublicKey, PublicKeyXOnly,
verify_signature, Context/GLOBAL_CONTEXT and the helpers in coincurve.ecdsa, with the same
signatures, defaults and error messages. For most code, changing the import is all there is to do:
-from coincurve import PrivateKey, PublicKey
-from coincurve.ecdsa import cdata_to_der, deserialize_recoverable, recoverable_convert
+from bitcurve import PrivateKey, PublicKey
+from bitcurve.ecdsa import cdata_to_der, deserialize_recoverable, recoverable_convert
The outputs are byte for byte identical, which the test suite checks against coincurve (see Tests). The differences:
custom_nonceis not supported, signatures always use RFC6979 nonces. Passing anything but the default raisesValueError.Contextis accepted everywhere for compatibility but carries no state, bitcurve uses one randomized libsecp256k1 context per thread.- Where coincurve passes cffi structs, bitcurve passes bytes or opaque objects:
PublicKey.public_keyis a parsedRawPublicKey, and thebitcurve.ecdsahelpers take and return signatures as bytes (64 byte compact or 65 byte recoverable). Chains likecdata_to_der(recoverable_convert(deserialize_recoverable(sig)))work unchanged. PrivateKey.public_keyandPrivateKey.public_key_xonlyare derived on first access instead of in the constructor.
Benchmarks
benchmarks/bench.py runs the same operations with coincurve and bitcurve on the shared test
vectors in tests/vectors/ (64 keys and messages, plus the official BIP340 vectors). Before timing
anything it checks that both libraries return identical results, then it reports the median time
per operation.
uv sync
uv run maturin develop --release --uv
uv run python benchmarks/bench.py # table
uv run python benchmarks/bench.py --markdown # markdown table, as below
uv run python benchmarks/bench.py --repeat 15 --json results.json
Build with --release first, a debug build is several times slower. coincurve only installs on
Python 3.13 and older, so that is where the comparison can run. CI runs the benchmark on every push
and writes the table to the job summary.
Results on an Intel Core i7-4790K @ 4.00GHz, Python 3.13.13, coincurve 21.0.0, median of 15 runs. Speedup above 1 means bitcurve is faster:
| Operation | coincurve | bitcurve | Speedup |
|---|---|---|---|
| PrivateKey + public key | 48.60 µs | 53.86 µs | 0.90x |
| PublicKey parse | 6.74 µs | 4.93 µs | 1.37x |
| ECDSA sign | 33.71 µs | 62.14 µs | 0.54x |
| ECDSA verify | 42.18 µs | 42.10 µs | 1.00x |
| verify_signature | 47.33 µs | 47.16 µs | 1.00x |
| ECDSA sign recoverable | 33.43 µs | 62.15 µs | 0.54x |
| ECDSA recover public key | 51.16 µs | 49.00 µs | 1.04x |
| bolt11 signature check | 49.76 µs | 48.18 µs | 1.03x |
| Schnorr sign | 87.83 µs | 148.37 µs | 0.59x |
| Schnorr verify | 41.73 µs | 42.56 µs | 0.98x |
| Schnorr verify (BIP340) | 40.60 µs | 41.05 µs | 0.99x |
| ECDH | 52.91 µs | 51.74 µs | 1.02x |
| PublicKey tweak add | 32.34 µs | 30.99 µs | 1.04x |
| PrivateKey to DER | 4.97 µs | 4.31 µs | 1.15x |
Everything that only uses public data (verification, recovery, parsing, ECDH and tweaks) is as fast as coincurve or slightly faster.
Why signing is about 1.8x slower
Signing is slower on purpose. Both libraries run the same libsecp256k1 code for the signature itself, the difference is what happens afterwards.
libsecp256k1 protects secret keys against side-channel attacks by blinding its computations with a
random value stored in the context. coincurve randomizes its context once, when it is created. The
secp256k1 crate goes further: after every operation that touches a secret key it rerandomizes the
context, mixing in fresh randomness and the secret itself, so the blinding never stays the same for
long. That costs about one extra elliptic curve multiplication, roughly 28 µs on the machine above:
- ECDSA signing (normal and recoverable) rerandomizes once: about 62 µs instead of 34 µs, 1.8x.
- Schnorr signing rerandomizes twice, once when the keypair is created and once when signing: about 148 µs instead of 88 µs, 1.7x.
- Creating a key rerandomizes once as well, but bitcurve only derives the public key that is actually used while coincurve computes both the normal and the x-only public key up front, so the difference is only 10%.
For the workloads bitcurve is written for, like signing an invoice or an LNURL-auth challenge, 28 µs per signature does not matter, the extra side-channel protection does. Verification, which servers do far more of, is not affected.
Development
Requires a Rust toolchain and uv.
uv sync # builds the extension in debug mode
uv run pytest
uv run maturin develop --release --uv # rebuild after changing src/lib.rs
cargo fmt --check && cargo clippy -- -D warnings
uv run ruff check . && uv run black --check . && uv run mypy
Tests
tests/test_vectors.pychecks bitcurve againsttests/vectors/coincurve.json, every output coincurve 21.0.0 produced for 64 deterministic keys and messages, and against the official BIP340 test vectors. It runs on every Python version, coincurve is not needed.tests/test_coincurve_compat.pycompares bitcurve with an installed coincurve on random inputs and checks that bitcurve has every public name coincurve has. Skipped where coincurve is not installed.tests/test_keys.pycovers error handling and edge cases.
scripts/generate_vectors.py regenerates tests/vectors/coincurve.json.
Releasing
Push a vMAJOR.MINOR.PATCH tag:
git tag v0.1.0
git push origin v0.1.0
The version only lives in the tag, Cargo.toml carries 0.0.0. The release workflow writes the
tag's version into Cargo.toml (scripts/stamp_version.py), builds abi3 wheels for Linux
(manylinux and musllinux, x86_64 and aarch64), macOS (x86_64 and arm64) and Windows (x64 and arm64)
plus the sdist, tests every wheel it can run, refuses to publish files that do not carry the tag's
version, uploads to PyPI with trusted publishing and creates a GitHub release.
PyPI trusted publishing has to be configured once for the repository and release.yml.
Credits
bitcurve would not exist without coincurve by Ofek Lev. Its API, documentation and behaviour are the model for bitcurve, and coincurve served as the reference implementation for the test vectors. Thank you for maintaining it for so many years. coincurve is licensed under MIT or Apache-2.0.
bitcurve is built on bitcoin-core/secp256k1 (MIT), the
rust-bitcoin/rust-secp256k1 crate (CC0-1.0) and
PyO3 (MIT or Apache-2.0). The BIP340 test vectors in
tests/vectors/bip340.csv are from bitcoin/bips
(BSD-2-Clause, MIT or CC0-1.0).
License
MIT
Metadata
Release files for bitcurve 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bitcurve-0.0.1.tar.gz | 136.3 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| bitcurve-0.0.1-cp310-abi3-win_arm64.whl | CPython 3.10 | abi3 | Windows ARM64 | Details |
| bitcurve-0.0.1-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| bitcurve-0.0.1-cp310-abi3-musllinux_1_2_x86_64.whl | CPython 3.10 | abi3 | Linux musl 1.2+ x86-64 | Details |
| bitcurve-0.0.1-cp310-abi3-musllinux_1_2_aarch64.whl | CPython 3.10 | abi3 | Linux musl 1.2+ ARM64 | Details |
| bitcurve-0.0.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| bitcurve-0.0.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| bitcurve-0.0.1-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
| bitcurve-0.0.1-cp310-abi3-macosx_10_12_x86_64.whl | CPython 3.10 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 12.3 MB
Release files / bitcurve-0.0.1.tar.gz
| Download URL | bitcurve-0.0.1.tar.gz |
|---|---|
| Size | 136.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
da899289f65efb0ffa7af933cfc02e949c8627b06b1fdb3c0ec769037a8a00e9
|
|
BLAKE2b-256 checksum How to use checksums |
b2dfc647ec84a494bd95b931f16be8d91fb4e099f26048db78a3b7ca9e88c93a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / bitcurve-0.0.1-cp310-abi3-win_arm64.whl
| Download URL | bitcurve-0.0.1-cp310-abi3-win_arm64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.10 Windows ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
b15216619cda2dcacc9f34b40424423e3ceb541d9e39df3a20ce5a503521ceab
|
|
BLAKE2b-256 checksum How to use checksums |
04f23738f8c6f823ca76f2df67302f3129d2ccad0d0d08241a921f7a01e60d5f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / bitcurve-0.0.1-cp310-abi3-win_amd64.whl
| Download URL | bitcurve-0.0.1-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
974ab159608c1aa68eafb6917bfe232780f2be7ba0f7d6ad37726d4d3676ce09
|
|
BLAKE2b-256 checksum How to use checksums |
407fca2ca6a89c43b9952c3e39ff27da8d8f70751de93c1f881ca686e5c64dd7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / bitcurve-0.0.1-cp310-abi3-musllinux_1_2_x86_64.whl
| Download URL | bitcurve-0.0.1-cp310-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 1.7 MB |
| Tags | CPython 3.10 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
0d6205ddd61ecc1fa59821c968f43188a6469855f9a0d8e5b53685827f5e6031
|
|
BLAKE2b-256 checksum How to use checksums |
a85ef53b072913a8bbe768f10e14cb879c06f7bd8e275f58de1f861c369f54f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / bitcurve-0.0.1-cp310-abi3-musllinux_1_2_aarch64.whl
| Download URL | bitcurve-0.0.1-cp310-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 1.7 MB |
| Tags | CPython 3.10 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
35a542dbbbf495dcaf61794847f261a9295f757640f1d2e00a28ce6e943e5c78
|
|
BLAKE2b-256 checksum How to use checksums |
1e10107d880412fa9421514a5e97a71c5a3df1aeb133a0c2efcd44b2ca48d8c6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / bitcurve-0.0.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | bitcurve-0.0.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.5 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
dfc1826f41ee16dfbfbe817e617c591ebe5b22468bd84b7a2f08675e9cf56782
|
|
BLAKE2b-256 checksum How to use checksums |
e457d4aec57a601e24f1b794fdf99acc788a051ee3e2db767d60f4bb71882827
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / bitcurve-0.0.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | bitcurve-0.0.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 1.5 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
9320219ed4ff0c6103b4cb8f9fb0bff01ea9a55a79938410a0172da70e4f6738
|
|
BLAKE2b-256 checksum How to use checksums |
5645a8e6dc069cd0b4e55c94288c1dd3f404a6cf20b11817d3992516a02d2875
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / bitcurve-0.0.1-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | bitcurve-0.0.1-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.5 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
fab91fd18f1167cf31da539e63bd513751db821d48ee62c3db0a2612a8dbd315
|
|
BLAKE2b-256 checksum How to use checksums |
2ac123ec28c64e3eb26b7a2b65df53978811a82eea8e7e35734f562258ccdc87
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / bitcurve-0.0.1-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | bitcurve-0.0.1-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 1.5 MB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
7431c95234eb79cc0374cac5b88e6a3f5a58c86796c5198ddb69be8b5e2237ef
|
|
BLAKE2b-256 checksum How to use checksums |
398f04ff26b382e0a75f980e537de50739e9fceba67567c221d3bd56346c753b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|