feat: derive self-certifying Willow DIDs#3
Merged
Conversation
The chain's RegisterDid check now requires new DID ids to be
self-certifying, so the old formats this SDK emitted
(did:willow:<alg>:<pubkey_prefix>) are rejected on-chain and
SDK-driven registration fails.
Derive the id from the public key instead:
did = "did:willow:z" + base58btc(SHA3-256(multicodec_prefix || pubkey))
- FIPS-202 SHA3-256 (hashlib.sha3_256), not Keccak-256.
- multicodec prefix 0xED01 (Ed25519) / 0xE701 (secp256k1); secp256k1
hashes the 33-byte compressed key (uncompressed input is normalised).
- base58btc (Bitcoin alphabet), leading 0x00 bytes -> '1', with the
multibase 'z' marker.
Add derive_did() so a DID can be computed from a public key alone,
which enables the pre-fund step of onboarding, and keep generate_did()'s
return shape unchanged (the id is just derived now). Document the
two-step bootstrap (pre-fund the derived id, then register) on the
register_did paths and in the README.
Add a unit test asserting the mandatory Ed25519 acceptance vector.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The chain's
RegisterDidcheck now requires new DID ids to be self-certifying (bound to the public key). This SDK still emitted the old formats —did:willow:<alg>:<pubkey_prefix>— so SDK-driven registration was being rejected on-chain.What
Replace the chosen/prefix id with the exact derivation the chain enforces:
hashlib.sha3_256) — not Keccak-256 (these differ; using keccak would be a bug).0xED 0x01(Ed25519) /0xE7 0x01(secp256k1). secp256k1 hashes the 33-byte compressed key; the SDK's 64-byte uncompressed wire key is normalised to compressed before hashing.0x00byte encoded as1, with the multibasezmarker.public_key_idstays"{did}#key-1".API
generate_did(...)keeps its return shape; thedidis just derived now (the DID document,public_key, signing/verification are all unchanged).derive_did(public_key, algorithm)(hex or bytes) computes the id from a public key alone. This is what makes the two-step bootstrap possible: because the id is known before registration, a funded account pre-funds the derived id (>= the registration fee), then the holder registers (fee paid from that balance). Documented on bothregister_didpaths and in the README. No chosen-id API was added.Acceptance vector (mandatory)
tests/test_auth.py::TestSelfCertifyingDid::test_ed25519_acceptance_vectorasserts:Ran locally and passed (full suite: 289 passed, 13 skipped/network). No new runtime dependencies — SHA3-256 is stdlib and base58btc is a small inline encoder.
Known limitation / follow-up (out of scope here)
The self-certifying id no longer embeds the algorithm, so
detect_algorithm_from_did()can no longer recover it from the bare DID string and defaults to Ed25519. For secp256k1 per-request auth,sign_request()would therefore pick the wrong algorithm. This SDK is Ed25519-first (the acceptance vector is Ed25519), so this doesn't affect the default path, but a follow-up should thread the algorithm throughset_identity/sign_request(or read it from the DID document'skey_type) rather than parsing it from the id.🤖 Generated with Claude Code