1. 概述
1.1. Sidetree
Sidetree 是一个基于现有区块链的第二层(L2)协议,专门用于去中心化身份(DID)的管理。
目前 DIF 中有两个基于区块链的、使用 Sidetree 协议的 DID 实现:
- ION:微软开源 的 ION 项目,基于比特币的、使用 Sidetree 协议的 DID 实现。
- Element:Transmute Industries 与 ConsenSys 合作的项目,基于以太坊的、使用 Sidetree 协议的 DID 实现。
1.2. DID
去中心化身份(Decentralized ID, DID)用来解决目前中心化身份系统的一系列问题,W3C 制定了一套 DID 的标准:W3C DID Spec,而DIF则基于此标准给出了 DID 的实现方案。
简单来说,did 是类似如下的一个字符串,用户使用此字符串来标识自己的身份。
DID 解决方案使用 DID 文档、可验证声明、PKI 体系等来解决去中心化身份管理的五大核心挑战:
- 表示:用来描述主体身份的可迁移表示
- 持久化:用来存储、提取主体身份的机制,同时还需要保持其隐私
- 隐私:在去中心化账本中保护主体身份的模型
- 断言: 确定主体身份的特定语句
- 解析:解析、验证特定主体身份的机制
2. Sidetree 协议
2.1. 协议概述
区块链的 TPS 一般都比较低,例如比特币的 TPS 大概为 7~8,所以如果把 DID 相关数据上链,那么会遇到严重的性能问题。所以,出现了 Sidetree 协议,它是一个区块链 L2 层的协议,核心思想是把 DID 的批量操作打包进一个区块链交易中,从而显著的提高 DID Operation 的数目。
2.2. 工作原理
- Sidetree 节点互相连接构成一个 L2 层的 P2P 网络,每个 Sidetree 节点都对外暴露 Restful API 来处理 DID 的 CURD 操作。
- Sidetree 节点尽可能多的收集 DID 操作,然后把这批操作打包,并创建一个 L1 链上交易并在交易中嵌入该操作批次的哈希。
- 批操作的源数据会推送到内容寻址存储(IPFS)上。当其他节点获知嵌入 Sidetree 操作的底层链上交易后,这些节点将向原始节点或其他 IPFS 节点请求该批次数据。
- 当一个节点收到某个批次后,它会将元数据固定到本地,然后 Sidetree 核心逻辑模块解压批次数据来 解析并验证其中的每个操作。
需要注意的是:目标链的区块/交易体系是 Sidetree 协议唯一需要的共识机制,不需要额外的 区块链、侧链或咨询权威单元来让网络中的 DID 达成正确的 PKI 状态。
2.3. Sidetree 协议的 DID 操作
2.3.1. DID OP
DID 操作不外乎 CURD。
2.3.2. Batch File 和 Anchor File
如下所示,把批操作的源数据推送到内容寻址存储(IPFS)上时,会存在两种文件:Batch File
和 Anchor File
:
Batch File:
{
"operations": [
"Encoded operation",
"Encoded operation",
...
]
}
Anchor File:
{
"batchFileHash": "Encoded multihash of the batch file.",
"didUniqueSuffixes": ["Unique suffix of DID of 1st operation", "Unique suffix of DID of 2nd operation", "..."],
"merkleRoot": "Encoded multihash of the root of the Merkle tree constructed from the operations included in the batch file."
}
可以看到,Batch File
是原始数据,Anchor File
是 Batch File
的元数据,而 L1 链上存储的是 Anchor File
的 hash。
为什么要这样设计呢?
这其实是为了后续实现 Sidetree 轻节点预留的,因为进行 DID 解析(例如 DID URL Dereferrence)的时候,如果只是需要一些元数据,那么只需下载Anchor File
即可,而不需要把较大的Batch File
下载下来。
2.4. Sidetree REST API
实现 Sidetree 协议的节点需要提供 REST API,且所有的请求都需要使用 JWS 签名。
提供的接口列表如下,详细的可参考:sidetree-rest-api
- DID and DID Document Creation
- DID Document resolution
- Updating a DID Document
- DID Deletion
- DID Recovery
- Fetch the current service versions (optional)
3. Sidetree 协议 的 Node.js 实现
3.1. 整体架构
Sidetree Node.js 实现的整体架构如下图所示,可以认为有三部分组成:
- Sidetree 节点:实现 Sidetree 协议的节点
- 区块链及其适配器
- CAS(Content Address Storage)存储,目前是 IPFS
3.2. 分层设计
为了解决 Sidetree 协议的后向兼容性,采取分层实现。
- Orchestration Layer (编排层):与协议版本无关的通用模块放在此层中实现。
- Protocol Version Specific Layer (版本适配层):与协议版本相关的模块放在此层中实现。
3.3. REST API
3.3.1. Blockchain REST API
- Get latest blockchain time
- Get blockchain time by hash
- Fetch Sidetree transactions
- Get first valid Sidetree transaction
- Write a Sidetree transaction
- Fetch normalized transaction fee for proof-of-fee calculation
- Fetch the current service version
3.3.2. CAS REST API
- Read content
- Write content
- Fetch the current service version
4. ION: 基于比特币的、使用 Sidetree 协议的 DID 实现
4.1. 使用 ION(macOS)
参考官方教程:ION Installation Guide
(1) 启动比特币客户端,并同步测试网
$ cat bitcoin.conf
testnet=1
server=1
rpcuser=hello2mao
rpcpassword=123
$ bitcoind -datadir=/xxx/btc
(2) 启动 MongoDB
$ cat /usr/local/etc/mongod.conf
systemLog:
destination: file
path: /usr/local/var/log/mongodb/mongo.log
logAppend: true
storage:
dbPath: /usr/local/var/mongodb
net:
bindIp: 127.0.0.1
$ mongod --config /usr/local/etc/mongod.conf
(3) 下载 ION 并 build
git clone https://github.com/decentralized-identity/ion
cd ion
npm run build
(4) 启动 Sidetree 的区块链适配层微服务,ION 是比特币的实现
npm run bitcoin
(5) 启动 Sidetree 的 CAS,ION 是 IFPS 网络的适配层微服务
npm run ipfs
(6)启动 Sidetree 核心服务
npm run core
(7)创建 ION DID 使用 ION 的 js sdk 创建 ION DID,如下:
var didAuth = require("@decentralized-identity/did-auth-jose");
var http = require("http");
async function createIONDid() {
// gen key
const kid = "#key-1";
const jwkPriv = await didAuth.EcPrivateKey.generatePrivateKey(kid);
const jwkPub = jwkPriv.getPublicKey();
jwkPub.defaultSignAlgorithm = "ES256K";
// load JWK into an EcPrivateKey object
const privateKey = didAuth.EcPrivateKey.wrapJwk(jwkPriv.kid, jwkPriv);
// construct the JWS payload
const body = {
"@context": "https://w3id.org/did/v1",
publicKey: [
{
id: jwkPub.kid,
type: "Secp256k1VerificationKey2018",
publicKeyJwk: jwkPub
}
],
service: [
{
id: "IdentityHub",
type: "IdentityHub",
serviceEndpoint: {
"@context": "schema.identity.foundation/hub",
"@type": "UserServiceEndpoint",
instance: ["did:test:hub.id"]
}
}
]
};
// Construct the JWS header
const header = {
alg: jwkPub.defaultSignAlgorithm,
kid: jwkPub.kid,
operation: "create",
proofOfWork: "{}"
};
// Sign the JWS
const cryptoFactory = new didAuth.CryptoFactory([
new didAuth.Secp256k1CryptoSuite()
]);
const jwsToken = new didAuth.JwsToken(body, cryptoFactory);
const signedBody = await jwsToken.signAsFlattenedJson(privateKey, { header });
// Print out the resulting JWS to the console in JSON format
console.log("Request: \n" + JSON.stringify(signedBody));
console.log("\n");
const data = JSON.stringify(signedBody);
var options = {
host: "127.0.0.1",
port: 3000,
path: "/",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": data.length
}
};
var req = http.request(options, function(res) {
// console.log("STATUS: " + res.statusCode);
// console.log("HEADERS: " + JSON.stringify(res.headers));
res.setEncoding("utf8");
res.on("data", function(chunk) {
console.log("Response: \n" + chunk);
});
});
req.on("error", function(e) {
console.log("problem with request: " + e.message);
});
// write data to request body
req.write(data);
req.end;
}
createIONDid();
Output:
Request:
{
"header": {
"alg": "ES256K",
"kid": "#key-1",
"operation": "create",
"proofOfWork": "{}"
},
"payload": "eyJAY29udGV4dCI6Imh0dHBzOi8vdzNpZC5vcmcvZGlkL3YxIiwicHVibGljS2V5IjpbeyJpZCI6IiNrZXktMSIsInR5cGUiOiJTZWNwMjU2azFWZXJpZmljYXRpb25LZXkyMDE4IiwicHVibGljS2V5SndrIjp7Imt0eSI6IkVDIiwia2lkIjoiI2tleS0xIiwiY3J2IjoiUC0yNTZLIiwieCI6Ikp6UTNiQWZmUzc2Y3R3dEJ4S0NBbnhMcXcyckRlaEd3eU9POGwta1dNclkiLCJ5IjoiSGlLb0xwbWdEVXhHSkhQdHJseHkzd2JPREZhWHA5OHhXUndleGRnTWlFVSIsImRlZmF1bHRFbmNyeXB0aW9uQWxnb3JpdGhtIjoibm9uZSIsImRlZmF1bHRTaWduQWxnb3JpdGhtIjoiRVMyNTZLIn19XSwic2VydmljZSI6W3siaWQiOiJJZGVudGl0eUh1YiIsInR5cGUiOiJJZGVudGl0eUh1YiIsInNlcnZpY2VFbmRwb2ludCI6eyJAY29udGV4dCI6InNjaGVtYS5pZGVudGl0eS5mb3VuZGF0aW9uL2h1YiIsIkB0eXBlIjoiVXNlclNlcnZpY2VFbmRwb2ludCIsImluc3RhbmNlIjpbImRpZDp0ZXN0Omh1Yi5pZCJdfX1dfQ",
"signature": "MEYCIQDIrTPcCV35zQRojk8KtlMAsbJKsbnMt8uEOD0XUspOUwIhAIbeS1r9dPU6cGvyNnWbChGR36HRG3VILr78M39xeG1H"
}
Response:
{
"@context": "https://w3id.org/did/v1",
"publicKey": [
{
"id": "#key-1",
"type": "Secp256k1VerificationKey2018",
"publicKeyJwk": {
"kty": "EC",
"kid": "#key-1",
"crv": "P-256K",
"x": "JzQ3bAffS76ctwtBxKCAnxLqw2rDehGwyOO8l-kWMrY",
"y": "HiKoLpmgDUxGJHPtrlxy3wbODFaXp98xWRwexdgMiEU",
"defaultEncryptionAlgorithm": "none",
"defaultSignAlgorithm": "ES256K"
}
}
],
"service": [
{
"id": "IdentityHub",
"type": "IdentityHub",
"serviceEndpoint": {
"@context": "schema.identity.foundation/hub",
"@type": "UserServiceEndpoint",
"instance": [
"did:test:hub.id"
]
}
}
],
"id": "did:ion:test:EiBNbUbOyzSmE66Akhc-6fYoo_A6QPF15VHSRNFLIJgUsw"
}
则新建的 DID 为:did:ion:test:EiBNbUbOyzSmE66Akhc-6fYoo_A6QPF15VHSRNFLIJgUsw
(8)查询 ION DID 使用 ION 的 js sdk 查询 ION DID,如下:
const http = require("http");
const options = {
hostname: "127.0.0.1",
port: 3000,
path: "/did:ion:test:EiBNbUbOyzSmE66Akhc-6fYoo_A6QPF15VHSRNFLIJgUsw",
method: "GET"
};
const req = http.request(options, res => {
// console.log("statusCode:", res.statusCode);
// console.log("headers:", res.headers);
res.on("data", d => {
console.log("Response: \n" + d);
});
});
req.on("error", e => {
console.error(e);
});
req.end();
Output:
Response:
{
"document": {
"@context": "https://w3id.org/did/v1",
"publicKey": [
{
"id": "#key-1",
"type": "Secp256k1VerificationKey2018",
"publicKeyJwk": {
"kty": "EC",
"kid": "#key-1",
"crv": "P-256K",
"x": "JzQ3bAffS76ctwtBxKCAnxLqw2rDehGwyOO8l-kWMrY",
"y": "HiKoLpmgDUxGJHPtrlxy3wbODFaXp98xWRwexdgMiEU",
"defaultEncryptionAlgorithm": "none",
"defaultSignAlgorithm": "ES256K"
}
}
],
"service": [
{
"id": "IdentityHub",
"type": "IdentityHub",
"serviceEndpoint": {
"@context": "schema.identity.foundation/hub",
"@type": "UserServiceEndpoint",
"instance": [
"did:test:hub.id"
]
}
}
],
"id": "did:ion:test:EiBNbUbOyzSmE66Akhc-6fYoo_A6QPF15VHSRNFLIJgUsw"
},
"resolverMetadata": {
"driverId": "did:ion:test",
"driver": "HttpDriver",
"retrieved": "2019-10-08T07:54:21.793Z",
"duration": "49.3152ms"
}
}
5. 参考
- Sidetree Protocol Specification
- ION
- Element
- DIF
- The Sidetree Protocol: Scalable DPKI for Decentralized Identity
- Sidetree - 去中心化身份管理协议
- Azure DID Project
- 去中心化身份(Decentralized ID, DID)介绍
- A Primer for Decentralized Identifiers