Skip to main content

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 secp256k1 crate, 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_nonce is not supported, signatures always use RFC6979 nonces. Passing anything but the default raises ValueError.
  • Context is 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_key is a parsed RawPublicKey, and the bitcurve.ecdsa helpers take and return signatures as bytes (64 byte compact or 65 byte recoverable). Chains like cdata_to_der(recoverable_convert(deserialize_recoverable(sig))) work unchanged.
  • PrivateKey.public_key and PrivateKey.public_key_xonly are 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.py checks bitcurve against tests/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.py compares 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.py covers 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)

Source distribution for bitcurve 0.0.1
File Size Uploaded
bitcurve-0.0.1.tar.gz 136.3 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for bitcurve 0.0.1
File
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}

Release history Release notifications | RSS feed

This release

0.0.1 This release

9 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page