IN PROGRESS
By Kim Hamilton Duffy (Learning Machine), Ryan Grant, and Christopher Allen
This assumes you are familiar with DID terminology at a basic level.
BTCR is a DID method that is based on the Bitcoin blockchain. The BTCR DID scheme uses a TX Ref-encoded (described below) transaction on the Bitcoin blockchain. The DID Description is constructed from a combination of the transaction details and an optional "continuation" DID Description, the address of which is stored in the OP_RETURN field. This could be a link to an IPFS address of a DID Description with additional entities.
The BTCR Hackathon readme has more context on BTCR DIDs beyond these basics. Note that at the time of the BTCR Hackathon, we were not yet using the new capabilities-based DID approach, so many details (e.g. control/owner keys) are out of date in the BTCR Hackathon repo.
Abbreviations:
- Bi = bitcoin address i
- Pi = public key i
- Si = signing key i (or private key i)
Creating the initial BTCR DID:
- Create key set (
B0
/P0
/S0
) - Create key set (
B1
/P1
/S1
) - Create Bitcoin transaction as follows:
- Output: Change address
B1
- Optional output:
OP_RETURN <link to DDO continuation>
- Signing key is
S0
, which reveals public keyP0
in the transaction
- Output: Change address
- Issue TX0 and wait for confirmation. Get TX Ref encoding of the transaction
TXREF(TX0)
At this point we have a DID of the format did:btcr:<TXREF(TX0)>
The initial BTCR DID is shown in the left side of this diagram.
The standard scheme for DIDs is:
did:<method>:<specific-idstring>
In this case, the method is "btcr". In the BTCR DID method, specific-idstring
is a TX Ref of confirmed transactions on a Bitcoin chain.
TX Refs are described in BIP 136 "Bech32 Encoded Transaction Position References" (bitcoin/bips#555). Among other advantages, they provide a concise way to refer to the confirmed transaction on a specific chain (testnet or mainnet) as a function of the block height and index.
The important difference is that txid is just a hash of the transaction, which may not yet be confirmed, and does not encode the chain, whereas TX Ref must be confirmed (since it is based on the block height and index).
DID consumers need to be able to construct a DID Description from a DID. In BTCR that works as follows:
- Given a DID, we know transaction reference (
did:btcr:<TXREF(TX0)>
) - Look up transaction. Is the transaction output spent?
- no: this is the latest version of the DID. From this we can construct the DID Description (described below)
- yes: keep following transaction chain until the latest with an unspent output is found
An entity updates the BTCR DID Description by spending the current transaction output. The BTCR Transaction Structure diagram shows how that is done in this second transaction.
- Create new tx like above, but send to
B2
- Set the OP_RETURN to the new DID Description
- Sign tx with
S1
(P1 is revealed)
This section demonstrates BTCR DIDs and DID Descriptions using the default example shown in the BTCR Playground.
The playground supports looking up BTCR DID Description for both mainnet and testnet chains. In general, we work with the testnet chain in these examples as we experiment with and develop BTCR.
The playground supports 3 means of entering a transaction:
- TX Ref (note that we don't need to enter the chain; described below)
- TXID and chain (testnet or mainnet)
- TX block height, index, and chain (testnet or mainnet)
To start, click "Convert from TX Ref" on the site with the default TX Ref.
The DID Description resulting from DID txtest1-xkyt-fzgq-qq87-xnhn
is listed below.
{
"@context": [
"https://schema.org/",
"https://w3id.org/security/v1"
],
"authorization": [
{
"capability": "UpdateDidDescription",
"permittedProofType": [
{
"proofType": "SatoshiBlockchainSignature2017",
"authenticationCredential": [
{
"type": [
"EdDsaSAPublicKey",
"CryptographicKey"
],
"hash-base58check": "mvq9zXGAr76uSoRG5ybEdECuXoPGY42ihh"
}
]
}
]
},
{
"capability": "IssueCredential",
"permittedProofType": [
[
{
"type": [
"EdDsaSAPublicKey",
"CryptographicKey"
],
"publicKeyHex": "0280e0b456b9e97eecb8028215664c5b99ffa79628b60798edd9d562c6db1e4f85",
"owner": "did:btcr:xkyt-fzgq-qq87-xnhn",
"id": "did:btcr:xkyt-fzgq-qq87-xnhn/keys/fundingKey"
}
]
],
"entity": "did:btcr:xkyt-fzgq-qq87-xnhn"
},
{
"capability": "IssueCredential",
"entity": "did:btcr:xkyt-fzgq-qq87-xnhn",
"permittedProofType": [
[
{
"id": "did:example:12345678/keys/1",
"type": "RsaCryptographicKey",
"owner": "did:example:12345678",
"publicKeyPem": "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
}
]
]
}
],
"authenticationCredential": [
{
"type": [
"EdDsaSAPublicKey",
"CryptographicKey"
],
"curve": "secp256k1",
"publicKeyHex": "0280e0b456b9e97eecb8028215664c5b99ffa79628b60798edd9d562c6db1e4f85",
"owner": "did:btcr:xkyt-fzgq-qq87-xnhn",
"id": "did:btcr:xkyt-fzgq-qq87-xnhn/keys/fundingKey"
},
{
"id": "did:example:12345678/keys/1",
"type": "RsaCryptographicKey",
"owner": "did:example:12345678",
"publicKeyPem": "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
}
],
"signature": {
"type": "SatoshiBlockchainSignature2017",
"id": "did:btcr:xkyt-fzgq-qq87-xnhn",
"chain": "testnet"
}
}
The DID Description for DID txtest1-xkyt-fzgq-qq87-xnhn
was determined as follows.
From the TX Ref, we look up details about the Bitcoin transaction. To see how to do this, refer to the transaction details for the TXID this resolves to, 67c0ee676221d9e0e08b98a55a8bf8add9cba854f13dda393e38ffa1b982b833.
This transaction has an OP_RETURN data output. For Blockcypher this is in the data_string field: https://raw.githubusercontent.com/kimdhamilton/did/master/ddo.jsonld](https://raw.githubusercontent.com/kimdhamilton/did/master/ddo.jsonld]). That means this DID Description will include additional authorizations and authenticationCredentials listed in the target of that URL.
Important: the steps below assume that the partial DID Description is stored in an immutable store as opposed to github. If this were stored in github, the partial DID Description should be signed.
This partial DID Description grants the following abilities:
- Two entities may issue credentials.
- One is an entity (currently) without an id, but described by its authentication method (
SatoshiBlockchainSignature2017
) and its hex-encoded public key. Note this is the same as the key used to sign the transaction. - One is an entity defined in a separate DID Description, with a
proofType
ofRsaSignature2017
- One is an entity (currently) without an id, but described by its authentication method (
- Two entities may authenticate (as a TBD DID). These entities are similar to above.
{
"@context": "https://w3id.org/btcr/v1",
"authorization": [
{
// gives the entity with TBD DID the ability to issue credentials where the "issuer" field is TBD DID. TODO: This could be a default ability.
"capability": "IssueCredential",
"permittedProofType": [
{
// Would we need a different type for off-chain signatures?
"proofType": "SatoshiBlockchainSignature2017",
"authenticationCredential": [
{
"type": [
"EdDsaSAPublicKey",
"CryptographicKey"
],
"publicKeyHex": "0280e0b456b9e97eecb8028215664c5b99ffa79628b60798edd9d562c6db1e4f85"
}
]
}
]
},
{
// enables an entity to issue credentials where the "issuer" field is TBD DID as long as this specific RSA key is used
"capability": "IssueCredential",
"entity": "did:example:12345678",
"permittedProofType": [
{
"proofType": "RsaSignature2017",
"authenticationCredential": [
{
"id": "did:example:12345678/keys/1",
"type": "RsaCryptographicKey",
"owner": "did:example:12345678",
"publicKeyPem": "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
}
]
}
]
}
],
"authenticationCredential": [
{
"type": [
"EdDsaSAPublicKey",
"CryptographicKey"
],
"curve": "secp256k1",
"publicKeyHex": "0280e0b456b9e97eecb8028215664c5b99ffa79628b60798edd9d562c6db1e4f85"
},
{
"id": "did:example:12345678/keys/1",
"type": "RsaCryptographicKey",
"owner": "did:example:12345678",
"publicKeyPem": "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
}
]
}
Again we're assuming the partial DID Description is stored in an immutable store. Because the content is immutable, the transaction signature signs the content hash as well. If not using content-addressable store, then another LD signature scheme would be used.
Because changing the content changes the address, and because the DID depends on the Bitcoin transaction reference, we have a bootstrapping problem where we cannot yet use the DID in the DID Description fragment shown above. We avoid this problem by omitting id
, entity
, and owner
where the DID is not yet known.
It's valid for a Linked Data object to omit an id; this is called a "blank node" and is the way you indicate you don't know the identifier yet, but you know its attributes.
With the partial DID Description and transaction details, we can form the complete DID Description.
- If an
authorization
is missing anentity
in the partial DID Description, update it with the now known DID (did:btcr:xkyt-fzgq-qq87-xnhn) - If an
authenticationCredential
is missing anowner
, update it with the now known DID (same as above) - If an
authenticationCredential
is missing anid
there are 2 cases:- If the credential's hex-encoded public key and proof type match that of the transaction signing key, populate the
id
withdid:btcr:xkyt-fzgq-qq87-xnhn/keys/fundingKey
. Note the/keys/fundingKey
path is a convention - Otherwise, populate the
id
withdid:btcr:xkyt-fzgq-qq87-xnhn/keys/i
, wherei
is the position of the authenticationCredential in the flattened partial DID Description. As of now, this is a very tentative convention.
- If the credential's hex-encoded public key and proof type match that of the transaction signing key, populate the
- Populate an authorization with the ability to update the DID Description from the transaction output
- A BTCR DID is updated by spending the transaction output. We can inspect the transaction to determine the output Bitcoin address
- At this point, we only reveal the hashed, base58 encoded version of the output key (i.e. the Bitcoin address)
- Note that we do not yet know the
entity
value; that will not exist until the DID Description is updated - We could create an
id
convention for this, e.gdid:btcr:xyv2-xzyq-qqm5-tyke/keys/output_address
- The method described above gives no default capabilities to an entity authenticating with the transaction signing key
- Capabilities are only granted if merged with a partial DID Description as described above
- We could consider granting default capabilities, but this does against the best practice of avoiding key reuse
- This means the BTCR DID is mostly useless without a partial DID Description, so we should consider this carefully
- Omitting
id
as opposed to the previous*
microsyntax introduces the problem above in which we don't know the path suffix to assign to keys. We can solve this in at least 2 ways:- Add a field to the BTCR DID method spec to address this. I.e.
path
and the assumption is that it will get merged with the DID to form theid
value. - Use an algorithmic approach like above, but I think that's a very bad idea
- Add a field to the BTCR DID method spec to address this. I.e.
- The number of confirmations of a transaction underlying a BTCR DID should be considered (and automated in the tool constructing the DID Description)
- Censorship resistance
- With IPFS URI of raw hash value; this is not-censorship resistant
- First BTCR DID does not need an OP_RETURN. This increases censorship resistance. Subsequently must have OP_RETURN