By: Kim Hamilton Duffy, Christopher Allen, Ryan Grant
This describes the process of resolving a BTCR DID into a DID Document. The draft reference implementation is available at https://github.com/WebOfTrustInfo/btcr-did-tools-js (see didFormatter.js). Note not all steps described in this document are implemented yet.
See the BTCR playground for a live demonstration. The BTCR playground uses the draft reference implementation BTCR DID resolver.
The input to a BTCR DID resolver is a BTCR DID.
Format: did:btcr:<specific-idstring>
Example: did:btcr:txtest1-xkyt-fzgq-qq87-xnhn
The initial step of a BTCR DID resolver validates input by confirming method
from the DID is btcr
.
- Confirm
method
from the DID isbtcr
. Fail if not
This phase constructs the "implicit" DID Document from Bitcoin transactioh data
- From the BTCR DID, extract the transaction reference: this is the
<specific-idstring>
part ofdid:btcr:<specific-idstring>
=did:btcr:<TXREF(TX0)>
- From the txref encoding, determine the transaction
- The txref prefix determines which Bitcoin network to use
- For example with
txtest1-xkyt-fzgq-qq87-xnhn
,txtest1
is the Bitcoin testnet network, which means we consult the testnet network for the transaction
- For example with
- The remainder of the txref-encoded transaction is an encoding of the block height and position
- Reference implementation: https://github.com/WebOfTrustInfo/txref-conversion-js
- The txref prefix determines which Bitcoin network to use
- Look up transaction by height and position. Is the transaction output spent?
- no: this is the latest version of the DID. From this we can construct the DID Document
- yes: keep following transaction chain until the latest with an unspent output is found
- Extract the hex-encoded public key that signed the transaction and update the DID document with default authentication capability
- Populate the first entry of the
publicKey
array in the DID document. This uses the Koblitz Elliptic Curve Signature 2016 signature suite - Populate the first entry of the
authentication
array in the DID document, referencing the key above. - Note: It is a BTCR method convention that
#keys-1
corresponds to the transaction signing key. We'll see in the next spec that overriding this path in the supplementary DID document data is not allowed
- Populate the first entry of the
- If the transaction contains an
OP_RETURN
field, populate theserviceEndpoint
in the DID document. This is assumed to reference supplementary DID document data- Add an entry to the
service
section of the DID documenttype
isBTCREndpoint
serviceEndpoint
is the value in the OP_RETURN field, e.g. "https://github.com/myopreturnpointer"timestamp
is XXX?
- Add an entry to the
- Add
SatoshiAuditTrail
, which contains additional metadata available from the Bitcoin transaction. Note: needs details
If the transaction contained no OP_RETURN data (and therefore no serviceEndpoint was added), the resolution process is done. Otherwise, proceed to the next phase.
The output of this resolution phase is referred to as the "implicit" DID Document; it is derived exclusively from Bitcoin transaction data.
If the transaction has no OP_RETURN data, then the service
array would have no entries. The only default capabilities would be to authenticate with the transaction signing key.
{
"@context": "https://w3id.org/btcr/v1",
"id": "did:btcr:<specific-idstring>",
"publicKey": [{
"id": "did:btcr:<specific-idstring>#keys-1",
"owner": "did:btcr:<specific-idstring>",
"type": "EdDsaSAPublicKeySecp256k1",
"publicKeyHex": "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71"
}],
"authentication": [{
"type": "EdDsaSAPublicKeySecp256k1Authentication",
"publicKey": "#keys-1"
}],
"service": [{
"type": "BTCREndpoint",
"serviceEndpoint": "https://github.com/myopreturnpointer.jsonld"
"timestamp": "XXXX"
]},
"SatoshiAuditTrail": [{
"chain": "testnet3",
"blockhash": "00000000b3487880b2814da8c0a6b545453d88945dc29a7b700f653cd7e9cdc7",
"blockindex": 1,
"outputindex": 1,
"blocktime": 1499502050,
"time": 1499501000,
"timereceived": 1499501000,
"burn-fee": -0.05
}]
}
- Retrieve the jsonld document from
serviceEndpoint.BTCREndpoint
, get first JSON type "DID Document". TODO- If URL doesn't exist, ERROR
- Authenticate this JSON-LD fragment as valid
- Note: The DID
id
value is not required to be in this JSON-LD.- If this patch data is an immutable file named by content hash (if stored in IPFS, for example) then it was committed before any blockchain confirmations occurred.
- The DID
id
value for an immutable DID document patch must be implicitly constructed by the method resolver presenting the valid DID document. - Question: must it be present if not in an immutable store? I think yes
- If BTCREndpoint is in immutable content hash (say ipfs), it is considered valid and no signature is required on this JSON, as in effect, it is signed by the blockchain transaction
- Otherwise, the JSON-LD at the BTCREndpoint must have signature matching Bitcoin transaction signing key
- If not, ERROR
- Question: does this mean BTCR resolvers must know in advance a registry of immutable stores?
- Note: The DID
- Merges in known JSON-LD values (additional keys, authorizations, etc) as appropriate into DID document. Additive only!
- Any fields that are part of the DID specification (
publicKey
,authentication
,service
) will be merged into the DID document by appending their entries to the arrays of the appropriate field - If continuation overwrites the Bitcoin key value, ERROR
- Unknown JSON-LD values (in type "DID Document") are appended to the constructed DID Document
- Non-DID JSON-LD data types that may also be at BTCEndpoint (such as Verifiable Claims) are ignored by resolver
- Any fields that are part of the DID specification (
- Loop for secondary (patch) DID Documents
- Get secondary (patch) DID Documents and merge according to step 8
- Parse for secondary BTCREndpoint URL(s) TODO: what does this mean?
- Questions:
- should we specify order? Depth first?
- what to do with path collisions? i.e. 2 different DID patches use the path
#keys-2
? - Consider DoS limits of this loop
- Proposed: wrap the DID document in resolver envelope with additional metadata
Returns final constructed JSON-LD DID Document to caller, which can use the keys to authenticate data such the signature on a verifiable claim, or perform other application tasks.
Let's assume the supplementary DID document (from the OP_RETURN data) is stored in an immutable store and contains the following didDocument
.
{
...
"didDocument": {
"@context": "https://w3id.org/did/v1",
"publicKey": [{
"id": "#keys-2",
"type": "RsaVerificationKey2018",
"publicKeyPem": "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
}],
"authentication": [{
// this key can be used to authenticate as DID ...fghi
"type": "RsaSignatureAuthentication2018",
"publicKey": "#keys-2"
}],
...
}
Note that the id
is not known yet, because the transaction referencing this supplementary document has not occurred.
Then the final DID document would be:
{
"resolver-specific envelope": "some-did-resolver-v1",
"doesn't matter to DID spec": true,
"resultDid": {
"@context": "https://w3id.org/did/v1",
"id": "did:btcr:<specific-idstring>",
"publicKey": [{
"id": "did:btcr:<specific-idstring>#key-1",
"owner": "did:btcr:mytxid",
"type": "EdDsaSAPublicKeySecp256k1",
"publicKeyHex": "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71"
}, {
"id": "did:btcr:<specific-idstring>#keys-2",
"type": "RsaVerificationKey2018",
"publicKeyPem": "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
}
],
"authentication": [{
"type": "EdDsaSAPublicKeySecp256k1Authentication",
"publicKey": "#keys-1"
}, {
"type": "RsaSignatureAuthentication2018",
"publicKey": "#keys-2"
}],
"service": [{
"type": "BTCREndpoint",
"serviceEndpoint": "https://github.com/myopreturnpointer",
"timestamp": "XXXX"
}],
"SatoshiAuditTrail": [{
"chain": "testnet3",
"blockhash": "00000000b3487880b2814da8c0a6b545453d88945dc29a7b700f653cd7e9cdc7",
"blockindex": 1,
"outputindex": 1,
"blocktime": 1499502050,
"time": 1499501000,
"timereceived": 1499501000,
"burn-fee": -0.05
}]
// fields found in BTCREndpoint marked using JSON type "DID Document"
"future DID-spec term_a" : {
},
"future DID-spec term_b: : {
}
// Excluded from this diddoc are some other things allowed in
// the files storing patches to the diddoc, when using it like a
// "bag of keys". One example would be verifiable claims stored
// in the same github document as a BTCREndpoint.
}
// Here the method resolver, sometimes also called the method
// driver, may add a signature (which is out of scope for the DID
// spec), confirming that it looked these things up on the
// blockchain then-current, and merged all applicable patches
// found following the BTCREndpoint algorithm. The resolver will
// have stripped any signature from a BTCREndpoint file on a
// mutable filesystem, since it MUST at minimum add its own
// SatoshiAuditTrail section.
}
- Content at BTCREndpoint has term separating out DID document "extensions".
- Under this are terms from DID specification.
- Known BTCR terms will be spliced into e.g. "service", "authentication", etc
- others will be included in DID returned by resolver
- everything not under that node is not part of DID Document.
- claims
- crypto-cats
- How to represent updates?
- Ryan claims that proofs about update are not proper for the DID document, other than to require methods to provide a way to do updates. Why should a client app need to know about updates? The DID spec should not try to provide a signed DID Document that is JSON-LD self-complete. Instead, trust in the method resolver is needed.
- Should this be part of the audit trail? (Ryan thinks yes. Any objections?)
- At which layer is this happening? is it a requirement of the universal resolver? or is this simply returning info from which this can be determined?
- This also means we can remove the following:
"update": {[
// update is implicit
// Optional: txout index
"type": "BTCRTip",
"serviceEndpoint": "bitcoinaddress"
]},
- How to represent timestamp?
- Some of the values (the DID ID itself, the bitcoin key, the BTREndpoint, and any immutable content hash items) are effectively timestamped by the Bitcoin transaction
- However, mutable values from the DID Document construction process may have been updated later.
- Should the BTCR spec demand that compliant method resolvers sign the returned DID Document (diddo), using a standard envelope? (This, even though it's out of scope for the DID spec.)