Warning: This is purely experimental and should not be used for production purposes.
A Python library for creating, managing, and resolving did:btcr2 decentralised identifiers anchored to the Bitcoin blockchain.
- DID Creation - Create deterministic (key-based) or external (hash-based) DIDs
- DID Resolution - Resolve DIDs by traversing Bitcoin blockchain history
- DID Updates - Update DID documents via beacon signals committed to Bitcoin transactions
- Multiple Networks - Supports Bitcoin mainnet, signet, testnet3, testnet4, regtest, and mutinynet
- Cryptographic Proofs - BIP340 Schnorr signature proofs with JCS canonicalization
libbtcr2/
├── did.py # DID identifier encoding/decoding (bech32)
├── did_manager.py # Core DID lifecycle management
├── resolver.py # DID resolution from blockchain
├── http_resolver.py # HTTPS binding DID resolver (FastAPI app)
├── routes.py # GET/POST /1.0/identifiers/{did} route handlers
├── beacon_manager.py # Bitcoin beacon signal creation
├── address_manager.py # Bitcoin address and UTXO management
├── esplora_client.py # Esplora blockchain API client
├── diddoc/
│ ├── doc.py # DID document model
│ ├── builder.py # DID document construction
│ └── updater.py # DID document updates via JSON-Patch
├── service.py # Beacon service definitions
├── multikey.py # Cryptographic key handling
├── models.py # Pydantic models (resolution results, network config, …)
├── errors.py # Custom exceptions
├── network_config.py # Built-in network definitions and Esplora endpoints
└── constants.py # Global constants and configurations
Requires Python >= 3.10.
python -m venv venv
source venv/bin/activatepip install -e .This installs the package from the main branch of the repository. As this is an experimental, proof-of-concept implementation there is no pip package available for the time being.
pip install libbtcr2@git+https://github.com/DCD/did-btcr2-pypytestlibbtcr2 ships a conformant DID Core HTTPS binding resolver built on FastAPI. It exposes the standard GET and POST /1.0/identifiers/{did} endpoints and honours the Accept header to return either a full resolution result or a bare DID document.
Use the provided script for a ready-to-run server:
python scripts/serve_resolver.py # binds to 0.0.0.0:8080
python scripts/serve_resolver.py --port 9000 # custom port
python scripts/serve_resolver.py --host 127.0.0.1 # localhost onlyPass a dict[str, BtcNetworkDefinition] to Btcr2Resolver to point the resolver at your own Esplora endpoints. The dict key is the network label encoded in the DID identifier.
from libbtcr2 import BtcNetworkDefinition, Btcr2Resolver
from libbtcr2.http_resolver import Btcr2HttpsResolver
network_definitions = {
"bitcoin": BtcNetworkDefinition(
btc_network="bitcoin",
esplora_api="https://mempool.space/api",
),
"regtest": BtcNetworkDefinition(
btc_network="regtest",
esplora_api="http://localhost:3000",
),
}
resolver = Btcr2Resolver(btc_network_definitions=network_definitions)
server = Btcr2HttpsResolver(host="0.0.0.0", port=8080, resolver=resolver)
server.run()The built-in DEFAULT_NETWORK_DEFINITIONS covers bitcoin, signet, mutinynet, and regtest with public Esplora endpoints and is used when no custom definitions are supplied.
| Method | Path | Description |
|---|---|---|
GET |
/1.0/identifiers/{did} |
Resolve a DID. Resolution options passed as query parameters. |
POST |
/1.0/identifiers/{did} |
Resolve a DID. Resolution options passed as a JSON body. |
Accept header |
Response body |
|---|---|
application/did-resolution |
Full resolution result: @context, didDocument, didResolutionMetadata, didDocumentMetadata |
application/did (or any other value) |
Bare DID document only |
application/did-url-dereferencing |
Dereferencing result (returns 501 Not Implemented — not yet supported) |
Resolve a DID (full result):
curl -H "Accept: application/did-resolution" \
http://localhost:8080/1.0/identifiers/did:btcr2:k1q5p72wesj9vtrtl5vu3l5hdx730vcp08xkn8kvznwf8plk5qpqkv2zg6pg50y{
"@context": "https://w3id.org/did-resolution/v1",
"didDocument": { "id": "did:btcr2:k1q...", ... },
"didResolutionMetadata": {},
"didDocumentMetadata": { "network": "bitcoin", "version": 1 }
}Resolve a DID (bare document):
curl -H "Accept: application/did" \
http://localhost:8080/1.0/identifiers/did:btcr2:k1q5p72wesj9vtrtl5vu3l5hdx730vcp08xkn8kvznwf8plk5qpqkv2zg6pg50yResolve with options via POST:
curl -X POST \
-H "Accept: application/did-resolution" \
-H "Content-Type: application/json" \
-d '{"versionId": 2}' \
http://localhost:8080/1.0/identifiers/did:btcr2:k1q5p72wesj9vtrtl5vu3l5hdx730vcp08xkn8kvznwf8plk5qpqkv2zg6pg50yResolve with sidecar data (external DID, no IPFS):
curl -X POST \
-H "Accept: application/did-resolution" \
-H "Content-Type: application/json" \
-d '{"sidecarData": {"initialDocument": { ... }}}' \
http://localhost:8080/1.0/identifiers/did:btcr2:x1q...Btcr2HttpsResolver.app is a plain FastAPI instance, so it can be mounted inside any ASGI application:
from fastapi import FastAPI
from libbtcr2.http_resolver import Btcr2HttpsResolver
app = FastAPI()
btcr2 = Btcr2HttpsResolver()
app.mount("/btcr2", btcr2.app)Example scripts are provided in the scripts/ directory:
scripts/serve_resolver.py- Start the HTTPS resolver serverscripts/regtest_create_deterministic.py- Create a deterministic DID from a secp256k1 key on regtestscripts/regtest_create_external.py- Create an external DID from an intermediate document on regtestscripts/mutinynet_create.py- Create a DID on mutinynetscripts/resolve.py- Resolve a DID and output the resolution result