1.3.0 • Published 2 years ago

datoms v1.3.0

Weekly downloads
-
License
MIT
Repository
-
Last release
2 years ago

datoms协议

Datoms是个人数据资产化协议堆栈中的最底层协议。

datom是最小的可交易数据资产单元

  • 数据资产的最小单元是datom。
  • 每一个datom都采用append-only log结构,具备很好的底层数据资产治理结构。
  • 每一个datom都可以设置对应的权限、隐私保护以及其他安全使用要求。
  • 每一个datom都是可交易的。
  • 每一个datom都可以单独确权。
  • 每一个datom都可以单独计量。
  • 每一个datom都可以单独进行权限设置、访问控制。

1. datom的结构

一个datom是具有如下标准结构的文件集合。通常包括六个文件(datoms v1.3.0版本):

  • 1.1 bitfield

    采用bitfield对datom的一些状态进行二进制编码(es6标准,前缀为0b)。

    state:
    		{
          var1: fales,
          var2: true,
          var3: false,
        }
    //上述状态用es6的bitfield表示:
    const state = 0b010 
  • 1.2 data

​ 采用wc3 VCs数据模型 的数据资产。一般包括原始数据,metadata以及(数据控制者)数字签名,可验证。VCs数据模型的结构示意图示例如下:

示例: Alice在JD的消费数据。
1. Alice的请求JD,复制其在JD在【2018/1/1-2019/1/1】的个人全部消费数据;
2. JD根据Alice的请求,按照peopledata的规范或行业规范,提供Alice上述数据。
3. 为保证复制到Alice后的数据可信,JD需要生成的数据格式示例如下:
{
  "@context": [         
    "https://www.peopledata.org.cn/2022/data/cusmerdata/v1"   //peopledata拟定的电子商务消费数据规范
  ],   
  
  "id": "metachain DID",  //由metachain分配给Alice的DID.
  
  //metadata
  
  "type": ["cusumer e-commerce data"],   //数据类型
  
  "issuer": "https://www.jd.com/xxxxxxxx",  //数据发布者:JD给Alice在JD的消费数据发布
  
  "issuanceDate": "2022-01-01T19:23:24Z",    //数据发布日期
  
  "rawdata": {                                   //原始数据
    
    "id": "did:jd:ebfeb1f712ebc6f1c276e12ec21",    // 原始数据的DID。
    
    "cusumerdata": {
      "startDate":2018/1/1 ,
      "endDate"": 2019/1/1 ,
      records:{
     //JD 自定义的个人消费数据规范格式的数据。
      	
      “时间”:xxxxx;
      .....
    		
    		
    	 }
    
     }
  
  
  "proof": {  //JD提供的签名proof,证明1)Alice的消费数据以及metadata是JD提供的;2)验证的方法;
    
    "type": "RsaSignature2018",
    
    "created": "2022-01-18T21:19:10Z",
    
    "proofPurpose": "assertionMethod",
    
    "verificationMethod": "https://www.jd.com/issuers/#key-1",
    
    "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..TCYt5X
      sITJX1CxPCT8yAV-TVkIEq_PbChOMqsLfRoPsnsgw5WEuts01mq-pQy7UJiN5mgRxD-WUc
      X16dUEMGlv50aqzpqh4Qktb3rk-BuQy72IFLOqV0G_zS245-kronKb78cPN25DGlcTwLtj
      PAYuNzVBAh4vGHSrQyHUdBBPM"
  }

//为防止中间传输过程中出现攻击/泄漏,提供的证明。
"proof": {
    "type": "RsaSignature2018",
    "created": "2022-01-18T21:19:10Z",
    "proofPurpose": "authentication",
    "verificationMethod": "did:JD:ebfeb1f712ebc6f1c276e12ec21#keys-1",
    
    "challenge": "1f44d55f-f161-4938-a659-f8026467f126",
    "domain": "4jt78h47fh47",
    "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5
      XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqs
      LfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh
      4vGHSrQyHUGlcTwLtjPAnKb78"
  }
}

对于dataom来说,这个data项是其“有效载荷”。

metadata主要来自数据发布者(控制者),提供metadata主要是对发布的个人数据提供可溯源、可验证的凭证。

proof主要有几种形式:

  • 数据发布者数字签名;
数据发布者用自己专用的私钥进行数字签名。并提供验证的方法(methold)。
验证者可以参考数据发布者公开的url,对此签名进行验证。
  • 中间人数字签名;
流通链条上经过的任何设备、实体的数字签名。
为溯源提供可验证的凭证。
  • Witness 方签名;
witeness方对任何需要见证的事件进行签名。
  • 交易签名;
对任何更新、更改事项(状态改变)的签名。
谁做的修改、更新。
  • ZKP零知识证明【可选】
采用ZKP-SNACK进行的non-interactive的零知识证明。
针对特定场景,需要非交互式的ZKP。
  • 1.3 key
由一个masterkey生成的private-key.
拥有此key,才能访问datom。
  • 1.4 secret_key 右crypto.keyPair()生成的key。
const crypto = require('datom-crypto')

//生成一对新的keyPair: {keyPair.PublicKey, keyPair.secretKey}

const keyPair = crypto.keyPair()
  
console.log('publicKey is ',keyPair.publicKey.toString('hex'))
console.log('secretKey is',keyPair.secretKey.toString('hex'))
  • 1.5 signatures
对数据块的签名。
signature = crypto.sign(data_block, secretKey)
  • 1.6 tree 该datom对应的merkle tree的hash(root_node).
对datom的数据块计算merkle tree。并把root hash存储在此。以作为后续审计和验证。
hash = crypto.data(data) 
hash = crypto.parent(left,right) 
{
  index: treeIndex,
  hash: hashOfThisNode,
  size: byteSizeOfThisTree
}
hash = crypto.tree(peaks) 
  • 1.7 可选项 (后续版本可以根据数据类型增加一些自定义添加项)。

    可自定义的添加项。

2. datoms

若干个datom(最小数据资产块)组合构成datoms。 datoms按照数据资产类别进行分类。可以是同一种类的数据,也可以不是同一类的。

每一个datom的结构如下:

datom:
- bitfield
- data
- key
- name
- sign
- tree

不同datom在一个space中。一个示例:

开始使用

  1. 安装软件包 系统配置要求: 1) node 14.0以上

npm i datoms

  1. 编写代码(示例)
const datoms = require('datoms')

const datom = toPromises(datoms('存储地址', {
    valueEncoding: 'json' // 数据采用json。也支持binary和其他函数对象。
  }))

//添加一个datom。可以添加任意多个。
await datom.append({
      name: 'name',
      Company: 'company',
      jobTitle: 'jobTitle',
      Tel: 'phoneNumbe',
      City: 'city',
      Address: 'streetAddress',
      emal: 'email',
      zipcode: 'zipCode'
})

//读取datom
datom.createReadStream()
        .on('data', console.log)
        .on('end', console.log.bind(console, '\n(end)'))

API

var datom = datoms(storage, [key],[options])

创建一个新的datom. storage为存储数据或metadata的目录。

var datom = datoms('./dir')   //数据存储到./dir目录中

key是datom的public key。如果不设置key,则默认从存储目录中的key文件中读取。如果没有key,则系统自动生成一个密钥对。

options包括:

{
  createIfMissing: true, // 如果在存储中没有key pair,则创建一个新的。
  overwrite: false, // 是否覆盖任何已经存在的datom
  valueEncoding: 'json' | 'utf-8' | 'binary', // data的类型
  sparse: false, // 不需要下载全部datom
  eagerUpdate: true, // 始终更新
  secretKey: buffer, // 自己传递secret key
  storeSecretKey: true, // 是否存secret key
  storageCacheSize: 65536, // 存储缓存最大entry的数量
  onwrite: (index, data, peer, cb) // 在数据验证后写入(选项) 
                                   // 
  stats: true // 收集网络统计信息
  // 自定义加密签名(可选)
  crypto: {
    sign (data, secretKey, cb(err, signature)),
    verify (signature, data, key, cb(err, valid))
  }
  noiseKeyPair: { publicKey, secretKey } // 设置noise通信协议需要的key pair
}

注意: keysecretkey是node.js的buffer instances,而非浏览器的arraybuffer instance。在浏览器中使用,需要用Feross's buffer模式。

const storage = someRandomAccessStorage
const myPublicKey = someUint8Array

const Buffer = require('buffer').Buffer
const PublicKeyBuffer = Buffer.from(myPublicKey.buffer)

const datom = datoms(storage, PublicKeyBuffer)

datom.append(data,[callback])

添加一个数据块到datom。 callback回调(err,seq)。seq是数据添加后返回的序列号。

const id = datom.get(index, [options], callback)

从datom调取一个数据块。 options包括:

{
  wait: true, // 等待索引下载完毕
  onwait: () => {}, //  如果等待下载,则hook
  timeout: 0, // 
  valueEncoding: 'json' | 'utf-8' | 'binary' // 
}

datom.getBatch(start, end, [options], callback)

读取一个区间范围内的数据块。 options包括:

{
  wait: sameAsAbove,
  timeout: sameAsAbove,
  valueEncoding: sameAsAbove
}

datom.cancel(getId)

取消一个正在pending的get操作。

datom.head([options], callback)

读取一个数据块的最新更新的。

const id = datom.download([range], [callback])

下载一个区间的数据。 range包含以下选项:

{
  start: startIndex,
  end: nonInclusiveEndIndex,
  linear: false // download range linearly and not randomly
}

如果不指定区间,则下载全部数据。

也可以指定特点的区块。

{
  blocks: [0, 1, 4, 10] // will download those 4 blocks as fast as possible
}

datom.undownload(downloadId)

取消一个正在penging的下载请求。

datom.signature([index], callback)

得到某个index数据块的签名,或整个stream的签名。

{
  index: lastSignedBlock,
  signature: Buffer
}

datom.verify(index, signature, callback)

验证一个index数据块的签名。

datom.rootHashes(index, callback)

取回一个index数据块的roothash。 回调返回(err,roots)。roots是merkle tree中的一组node。

Node {
  index: merkle tree的根节点的index.
  size: 这个root下的子node的总字节数
  hash: root下的子node的hash(32-byte buffer)
}

var number = datom.downloaded([start], [end])

返回从startend下载的数量。

var bool = datom.has(index)

查询是否存在index的数据块。

var bool = datom.has(start, end)

查询是否存在startend区间的数据块。

var stream = datom.createReadStream([options])

创建一个可以读取数据的streamjs

{
  start: 0, // 开始的index
  end: datom.length, // 得到结尾为止
  snapshot: true, // 每次是否都读到结尾为止
  tail: false, // 设定 `start` 到 `datom.length`
  live: false, // 设定保持读状态
  timeout: 0, // 每次事件的timeout (0 means no timeout)
  wait: true, // 当数据下载是等待
  batch: 1 // 每批次读取的message的数量
}

var stream = datom.replicate(isInitiator, [options])

创建一个replicate的stream。

// 假设有两个datoms, localdatom + remotedatom, 使用相同的key。
var net = require('net')
var server = net.createServer(function (socket) {
  socket.pipe(remotedatom.replicate(false)).pipe(socket)
})

// 在客户端
var socket = net.connect(...)
socket.pipe(localdatom.replicate(true)).pipe(socket)

Options include:

{
  live: false, // 当所有数据下载完后,是否保持replicate
  ack: false, // 当设定为true时:一个peer写操作时,会被告知。
  download: true, // 从peer下载数据吗?
  upload: true, // 从peer上传数据吗?
  encrypted: true, // 用key pair加密数据
  noise: true, // 是否采用noise加密通信p2p协议
  keyPair: { publicKey, secretKey }, // 用这个key pair为noise加密
  onauthenticate (remotePublicKey, done) //当远程主机认证时,hook这个key
}

datom.close([callback])

关闭一个datom。

datom.destroyStorage([callback])

删除全部datom并关闭。

datom.audit([callback])

审计datom中的所有数据。与merkle tree中的hash比较,如果不一致,则clear bitfield。

{
  valid: 10, // 有多少数据满足hash。
  invalid: 0, // 有多少数据不满足hash。
}

datom.writable

datom是否可写?

datom.readable

datom是否可读?

datom.key

datom的publiic key

datom.discoveryKey

datom的discoverykey

datom.length

datom的长度

datom.byteLength

datom的子节长度

datom.stats

datom的统计信息。

{
  totals: {
    uploadedBytes: 100,
    uploadedBlocks: 1,
    downloadedBytes: 0,
    downloadedBlocks: 0
  },
  peers: [
    {
      uploadedBytes: 100,
      uploadedBlocks: 1,
      downloadedBytes: 0,
      downloadedBlocks: 0
    },
    ...
  ]
}

datom.on('peer-add', peer)

当有新的peer加入时emmitted。

datom.on('peer-remove', peer)

当有peer退出时emmitted。

datom.on('peer-open', peer)

当有一个peer channel打开时emmitted。

datom.peers

所有peers的列表。

ext = datom.registerExtension(name, handlers)

注册一个replication extension。

{
  encoding: 'json' | 'binary' | 'utf-8' | anyAbstractEncoding,
  onmessage (message, peer) { //called 当收到peer发出的信息。
  },
  onerror (err) {
  }
}

ext.send(message, peer)

发出一个extension message给一个特点的peer。

ext.broadcast(message)

广播一个extension message个所有链接的peer.

peer.publicKey

peer的key.

datom.on('ready')

当datom就绪emitted。

datom.on('error', err)

当遇到错误emitted.

datom.on('download', index, data)

下载index的数据emitted。

datom.on('upload', index, data)

当数据上传时emitted.

datom.on('append')

当有新的数据appned时emitted。

datom.on('sync')

当数据从0到datom.length全部下载完后emitted。

datom.on('close')

当datom全部关闭时emitted。

附录:

License

@jerry.zhangjerry.zhang.bill@gmail.com

MIT