Skip to main content

BIP46 - Address Scheme for Timelocked Fidelity Bonds

PyPI

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)

Source distribution for bip46 1.1.0
File Size Uploaded Provenance
bip46-1.1.0.tar.gz 36.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for bip46 1.1.0
File Interpreter ABI Platform
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.

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files No provenance

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