BIP46 - Address Scheme for Timelocked Fidelity Bonds
Python implementation of BIP46: Address Scheme for Timelocked Fidelity Bonds.
Example
Create a redeem script from a mnemonic
from datetime import UTC, datetime
from bip46 import (
create_redeemscript,
hdkey_derive,
hdkey_from_mnemonic,
hdkey_to_pubkey,
lockdate_to_derivation_path,
redeemscript_address,
redeemscript_pubkey,
)
network = "mainnet"
lock_date = datetime(2042, 6, 1, tzinfo=UTC)
lock_path = lockdate_to_derivation_path(lock_date, network=network)
hdkey = hdkey_from_mnemonic(
"abandon abandon abandon abandon abandon abandon"
" abandon abandon abandon abandon abandon about",
network=network,
)
redeem_key = hdkey_derive(hdkey, lock_path)
redeem_pub_key = hdkey_to_pubkey(redeem_key)
redeem_script = create_redeemscript(lock_date, redeem_pub_key)
script_pubkey = redeemscript_pubkey(redeem_script)
script_address = redeemscript_address(script_pubkey, network=network)
Create addresses from an xpub
BIP46 paths include hardened account components: m/84'/coin_type'/0'/2/index.
A public key cannot derive those hardened components, so pass an account-level xpub
for m/84'/coin_type'/0'. The library will derive the public BIP46 suffix
2/index from that account xpub.
from datetime import UTC, datetime
from bip46 import (
create_redeemscript,
hdkey_derive,
hdkey_from_xpub,
hdkey_to_pubkey,
lockdate_to_derivation_path,
redeemscript_address,
redeemscript_pubkey,
)
network = "mainnet"
lock_date = datetime(2042, 6, 1, tzinfo=UTC)
lock_path = lockdate_to_derivation_path(lock_date, network=network)
hdkey = hdkey_from_xpub("xpub...")
redeem_key = hdkey_derive(hdkey, lock_path)
redeem_pub_key = hdkey_to_pubkey(redeem_key)
redeem_script = create_redeemscript(lock_date, redeem_pub_key)
script_pubkey = redeemscript_pubkey(redeem_script)
script_address = redeemscript_address(script_pubkey, network=network)
Create and sign a fidelity bond certificate
Per BIP46, the certificate message contains a certificate/identity pubkey (an online key, independent of the fidelity bond address), and is signed with the timelock private key. This lets the timelock key stay offline — only the identity key needs to be hot. The verifier recovers the timelock pubkey from the signature to confirm bond ownership.
import base64
from bip46 import (
create_certificate_message,
recover_from_signature_and_message,
sign_certificate_message,
)
# identity/certificate pubkey — can be any key, does not have to match the timelock key
cert_pubkey_hex = "02..."
# timelock private key (the offline key that owns the fidelity bond UTXO)
timelock_private_key = bytes.fromhex("...")
message = create_certificate_message(cert_pubkey_hex)
signature_bytes = sign_certificate_message(timelock_private_key, message)
signature_b64 = base64.b64encode(signature_bytes).decode()
# verify: recovered pubkey is the timelock pubkey, proving ownership of the bond
recovered_timelock_pubkey = recover_from_signature_and_message(signature_b64, message)
The CLI also accepts an account xpub:
XPUB="xpub..." uv run bip46 create-timelock 2042 6 mainnet
Scanning and verifying on-chain with electrs or mempool.space
bip46 is chain-query agnostic. Use any HTTP client against an electrs-compatible API (electrs, mempool.space, Blockstream Explorer) to look up addresses and UTXOs.
Check if a timelock address has funds (electrs)
import httpx
from bip46 import (
create_redeemscript,
hdkey_derive,
hdkey_from_mnemonic,
hdkey_to_pubkey,
lockdate_to_derivation_path,
redeemscript_address,
redeemscript_pubkey,
)
from datetime import UTC, datetime
ELECTRS = "https://blockstream.info/api" # or mempool.space/api
lock_date = datetime(2042, 6, 1, tzinfo=UTC)
hdkey = hdkey_from_mnemonic("your mnemonic ...", network="mainnet")
path = lockdate_to_derivation_path(lock_date, network="mainnet")
child = hdkey_derive(hdkey, path)
pubkey = hdkey_to_pubkey(child)
redeemscript = create_redeemscript(lock_date, pubkey)
address = redeemscript_address(redeemscript_pubkey(redeemscript), network="mainnet")
txs = httpx.get(f"{ELECTRS}/address/{address}/txs").raise_for_status().json()
print(f"{address}: {len(txs)} transaction(s)")
Find the UTXO for a timelock address
utxos = httpx.get(f"{ELECTRS}/address/{address}/utxo").raise_for_status().json()
for utxo in utxos:
print(utxo["txid"], utxo["vout"], utxo["value"], "sats")
Verify a fidelity bond proof (certificate + UTXO)
import base64
import httpx
from bip46 import verify_certificate
ELECTRS = "https://blockstream.info/api"
# received from the bond holder
cert_pubkey_hex = "02..." # identity pubkey embedded in the cert
cert_signature_b64 = "..." # base64 signature over the cert message
timelock_address = "bc1q..." # the fidelity bond address
# 1. verify the certificate — recovered pubkey must match the timelock address's key
# (derive the expected timelock pubkey from the address or from the redeemscript)
expected_timelock_pubkey: bytes = ... # hdkey_to_pubkey(hdkey_derive(hdkey, path))
assert verify_certificate(cert_signature_b64, cert_pubkey_hex, expected_timelock_pubkey)
# 2. confirm the address actually holds a UTXO on-chain
utxos = httpx.get(f"{ELECTRS}/address/{timelock_address}/utxo").raise_for_status().json()
assert utxos, "no unspent output found for timelock address"
total_sats = sum(u["value"] for u in utxos)
print(f"bond value: {total_sats} sats")
Metadata
Release files for bip46 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| bip46-1.1.0.tar.gz | 36.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bip46-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.4 kB
Release files / bip46-1.1.0.tar.gz
| Download URL | bip46-1.1.0.tar.gz |
|---|---|
| Size | 36.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
05832f96c086be6d8ef55dad7fdc085363dfd824250a2bd5333e5a76a829fe65
|
|
BLAKE2b-256 checksum How to use checksums |
e1279f3e21bc2125822f2d0de9b70f7546207f14eede9b2f6343a1edf1c7a8a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","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 / bip46-1.1.0-py3-none-any.whl
| Download URL | bip46-1.1.0-py3-none-any.whl |
|---|---|
| Size | 11.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6312d8a54cb5c1f7e87da967f199e272c904cb2966b72e4ffc4b258f8ffe0cde
|
|
BLAKE2b-256 checksum How to use checksums |
e080151b3e880a502a7d2769fcd127bcf5ebdf0db1a26325114aedd1788b4b11
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","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}
|
Security for bip46 1.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.