An incremental CLI tool for generating did:btcr2 test vectors through a stepped workflow: create -> update (offline) -> fund -> announce -> resolve. It produces JSON files under lib/data/.
The first positional argument is the action. create runs offline. All subsequent actions only need --hash: the type and network are derived from the DID itself.
# From packages/method/
# 1. Create a new DID (only action that takes --type and --network)
pnpm generate:vector create
pnpm generate:vector create --type external --network mutinynet
# 2. Construct and sign an update offline (use the hash printed by step 1)
pnpm generate:vector update --hash <hash> --offline
# 3. Fund the beacon address(es)
source .env
pnpm generate:vector fund --hash <hash>
# 4. Announce the signed update on-chain
pnpm generate:vector announce --hash <hash>
# 5. Resolve the DID against a live Bitcoin node
pnpm generate:vector resolve --hash <hash>
# List existing vectors
pnpm generate:vector list
pnpm generate:vector list --network regtest --type key
pnpm generate:vector <action> [options]
| Action | Description |
|---|---|
create |
Create a new DID and initial test vector files |
update |
Construct and sign an update (optionally announce) |
fund |
Fund beacon address(es) via RPC sendtoaddress + mine a block |
announce |
Announce a previously signed update on-chain |
resolve |
Resolve a DID against a live Bitcoin node |
list |
Show existing test vectors |
| Flag | Values | Default | Applies to | Description |
|---|---|---|---|---|
--type |
key, external |
key |
create, list |
DID identifier type |
--network |
regtest, bitcoin, mutinynet, etc. |
regtest |
create, list |
Bitcoin network |
--genesis |
hex string | prompt / auto-generate | create |
Genesis bytes hex (see below) |
--hash |
8-char short hash | n/a | update, fund, announce, resolve |
Vector identifier (required) |
--interactive |
flag (no value) | off | update |
Enable interactive patch builder |
--amount |
BTC amount | 0.001 |
fund |
BTC amount to send to each beacon address |
--offline |
flag (no value) | off | update, resolve |
Skip on-chain announcement or live resolution |
After
create, the hash uniquely identifies the vector. The script finds the directory automatically and derives the type and network from the stored DID.
createCreates a DID and writes the initial vector files. The --genesis flag behavior depends on the --type:
--genesis is a compressed public key hex. If omitted, prompts for one. If blank, auto-generates a keypair.--genesis is a SHA-256 hash hex of a genesis document. If omitted, prompts for a JSON genesis document or hex hash. If blank, auto-generates a keypair and default genesis document.# Auto-generate everything
pnpm generate:vector create
pnpm generate:vector create --type external --network regtest
# Bring your own genesis bytes
pnpm generate:vector create --type key --genesis 02abc...def
pnpm generate:vector create --type external --network regtest --genesis 82830a78...f83a99
Outputs:
lib/data/{network}/{type}/{hash}/
create/input.json # { idType, version, network, genesisBytes }
create/output.json # { did }
other.json # { genesisKeys: { secret, public }, genesisDocument? }
The --hash for subsequent steps is printed to the console.
updateReads back the create output, rebuilds the source document, constructs and signs an update.
Without --interactive: applies a default patch that rotates the first SingletonBeacon service endpoint (P2PKH key rotation).
With --interactive: prompts for JSON Patch operations with smart auto-generation (see below).
With --offline: builds and signs the update but skips the on-chain announcement. This is the typical workflow: use fund and announce as separate steps afterward.
Without --offline: also announces the update on-chain immediately (requires a funded beacon address and BITCOIN_NETWORK_CONFIG).
# Recommended: sign offline, then fund and announce separately
pnpm generate:vector update --hash <hash> --offline
pnpm generate:vector update --hash <hash> --offline --interactive
# Or sign and announce in one step (beacon must already be funded)
source .env
pnpm generate:vector update --hash <hash>
Outputs:
lib/data/{network}/{type}/{hash}/
update/input.json # { sourceDocument, patches, sourceVersionId, ... }
update/output.json # { signedUpdate }
other.json # (updated with generated keys)
fundFunds all beacon service addresses in the DID document via RPC sendtoaddress, then mines blocks to confirm the funding transaction(s). Requires a live Bitcoin node with a loaded wallet.
source .env
pnpm generate:vector fund --hash <hash>
pnpm generate:vector fund --hash <hash> --amount 0.01
Requires
BITCOIN_NETWORK_CONFIGto be set with connection info for the DID’s network. Source your.envfile or export it directly.
announceAnnounces a previously signed update on-chain via the beacon service. Reads the signed update and beacon metadata from the update step’s persisted files. Useful for retrying a failed announcement without re-running the full update pipeline.
source .env
pnpm generate:vector announce --hash <hash>
Requires
BITCOIN_NETWORK_CONFIGto be set with connection info for the DID’s network. Source your.envfile or export it directly.
resolveResolves a DID against a live Bitcoin node. Assembles a sidecar from the signed update (if the update step has been run) and the genesis document (for x1 types).
With --offline: writes only the sidecar input file without connecting to Bitcoin.
# Resolve against a live Bitcoin node
source .env
pnpm generate:vector resolve --hash <hash>
# Offline: build sidecar input only
pnpm generate:vector resolve --hash <hash> --offline
Outputs:
lib/data/{network}/{type}/{hash}/
resolve/input.json # { did, resolutionOptions: { sidecar } }
resolve/output.json # { didDocument, didResolutionMetadata, didDocumentMetadata } (live only)
Live resolution requires
BITCOIN_NETWORK_CONFIGto be set with connection info for the DID’s network. Source your.envfile or export it directly.
listDisplays existing test vectors filtered by network and type. If --network or --type are not provided, prompts interactively.
pnpm generate:vector list
pnpm generate:vector list --network regtest --type key
Pass --interactive to the update step to build custom patches. The tool detects common patch targets and auto-generates values.
pnpm generate:vector update --hash <hash> --interactive
/service/<n>)When add or replace targets a path like /service/0, the tool:
p2pkh, p2wpkh, p2tr): defaults to p2pkh--- Add a JSON Patch operation ---
op: add
path: /service/1
Detected service patch, auto-generating value.
address type (p2pkh | p2wpkh | p2tr) [p2pkh]: p2wpkh
pubkey hex (leave empty to auto-generate):
Auto-generated keypair (stored as "service-service-1")
Added: {"op":"add","path":"/service/1","value":{"id":"did:btcr2:...#service-1","type":"SingletonBeacon","serviceEndpoint":"bitcoin:bcrt1q..."}}
/verificationMethod/<n>)When add or replace targets a path like /verificationMethod/1, the tool:
someNewId or #someNewId): defaults to key-1, key-2, etc.publicKeyMultibase--- Add a JSON Patch operation ---
op: add
path: /verificationMethod/1
Detected verificationMethod patch, auto-generating value.
id fragment (e.g. "someNewId" or "#someNewId") [key-1]: recoveryKey
pubkey hex (leave empty to auto-generate):
Auto-generated keypair (stored as "verificationMethod-verificationMethod-1")
Added: {"op":"add","path":"/verificationMethod/1","value":{"id":"did:btcr2:...#recoveryKey","type":"Multikey","controller":"did:btcr2:...","publicKeyMultibase":"zQ3sh..."}}
For any path not matching the above patterns, or for operations like remove, move, copy, the tool falls back to manual JSON value input.
All generated and user-provided keys are persisted in other.json for later reuse:
secret and public hex valuespublic hex with an empty secret field for you to fill in if needed{
"genesisKeys": { "secret": "...", "public": "..." },
"newBeaconKeys": { "secret": "...", "public": "..." },
"generatedKeys": {
"service-service-1": { "secret": "...", "public": "..." },
"verificationMethod-verificationMethod-1": { "secret": "", "public": "..." }
}
}
lib/data/{network}/{type}/{hash}/
create/
input.json
output.json
update/
input.json
output.json
resolve/
input.json
output.json # (live mode only)
other.json