IPFS数据模型-IPLD
有许多系统使用merkle-tree
和hash-chain
受启发的数据结构(例如git,bittorrent,ipfs,tahoe-lafs,sfsro)。IPLD(星际链接数据)定义:
- merkle-links:merkle-graph的核心单元
-
merkle-dag:任何边为merkle-links的图。
dag
代表“有向无环图” - merkle-paths:使用命名的merkl-links来遍历merkl-dags的unix风格的路径。
- IPLD格式:可以表示IPLD对象的一组格式,例如JSON,CBOR,CSON,YAML,Protobuf,XML,RDF等。
- IPLD规范格式:一种序列化格式的确定性描述,确保相同的逻辑对象始终被序列化到相同的位序列。这对于链接和所有加密应用程序都是至关重要的。
介绍
什么是merkle-link?
merkl-link是两个对象之间的链接,它们是由目标对象的加密散列处理的,并嵌入到源对象中。merkl-links的内容寻址允许:
- 加密完整性检查:解析链接的值可以通过哈希来测试。反过来,这可以实现广泛,安全和可靠的数据交换(例如git或bittorrent),因为其他人不能给你任何不会散列到链接值的数据。
- 不可变的数据结构:带有merkle链接的数据结构不能改变,这对于分布式系统来说是一个不错的属性。这对于版本控制,表示分布式可变状态(例如CRDT)和长期存档很有用。
一个merkle-link在IPLD对象模型中由包含一个key/mapped
到“链接值”的映射表示。例如:
一个以json表示的“链接对象”的链接
{ "/" : "/ipfs/QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k" }
// "/" is the link key
// "/ipfs/QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k" is the link value
对象为foo/baz
的链接
{
"foo": {
"bar": "/ipfs/QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k", // not a link
"baz": {"/": "/ipfs/QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k"} // link
}
}
对象在files/cat.jpg/link
的实际链接以及files/cat.jpg
中的伪“链接对象”。
{
“ files ”: {
“ cat.jpg ”: { //将链接属性封装在另一个对象中
“ link ”: { “ / ”: “ / ipfs / QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k ” },//链接
“ mode ”: 0755,
“owner”: “ jbenet ”
}
}
}
当取消链接时,映射本身将被它指向的对象替换,除非链接路径无效。
该链接可以是multihash
,在这种情况下,假设它是/ipfs层次结构中的链接,或者直接指向对象的绝对路径。目前,只允许使用/ipfs层次结构。
如果应用程序想要将具有单个/key的对象用于其他目的,则应用程序本身应负责转义/IPLD对象中的/key,这样应用程序的key就不会与IPLD的特殊/key发生冲突。
什么是merkle-graph或merkle-dag?
带有merkl-links的对象形成一个Graph(merkle-graph),如果加密散列函数的属性保持不变,则这些对象必然都是定向的,并且可以认为它是非循环的,即merkle-dag。因此,所有使用merkle-linking(merkle-graph)的图必定也是有向无环图(DAG,因此为merkle-dag)。
什么是merkle路径?
merkl-path是一种unix风格的路径(例如,/a/b/c/d),它最初通过merkl-link进行引用,并允许访问被引用节点和其他节点的元素。
我们鼓励通用文件系统在IPLD上设计一个对象模型,该模型将专门用于文件操作,并有特定的路径算法来查询该模型。
merkle-paths如何工作?
merkl-path是一种unix风格的路径,最初通过merkl-link进行引用,然后在中间对象中命名merkl-links。名称后面的意思是查找对象,查找名称并解析相关的merkl-link。
例如,假设我们有这个merkle-path:
/ipfs/QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k/a/b/c/d
路径表述:
-
ipfs
是一个协议命名空间(允许计算机识别要做什么) -
QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k
是一个加密哈希。 -
a/b/c/d
是一个路径遍历,就像在unix中一样。
路径遍历,用符号表示/,发生在两种链接上:
- 对象内遍历遍历同一对象内的数据。
- 跨对象遍历从一个对象遍历到另一个对象,通过merkle-link解析。
例子
使用以下数据集:
> ipfs object cat --fmt=yaml QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k
---
a:
b:
link:
/: QmV76pUdAAukxEHt9Wp2xwyTpiCmzJCvjnMxyQBreaUeKT
c: "d"
foo:
/: QmQmkZPNPoRkPd7wj2xUJe5v5DsY6MX33MFaGhZKB2pRSE
> ipfs object cat --fmt=yaml QmV76pUdAAukxEHt9Wp2xwyTpiCmzJCvjnMxyQBreaUeKT
---
c: "e"
d:
e: "f"
foo:
name: "second foo"
> ipfs object cat --fmt=yaml QmQmkZPNPoRkPd7wj2xUJe5v5DsY6MX33MFaGhZKB2pRSE
---
name: "third foo"
路径的一个例子:
-
/ipfs/QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k/a/b/c
将遍历第一个对象并获取字符串d
。 -
/ipfs/QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k/a/b/link/c
将遍历两个对象并获取字符串e
-
/ipfs/QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k/a/b/link/d/e
遍历两个对象并获取字符串f
-
/ipfs/QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k/a/b/link/foo/name
遍历第一个和第二个对象并获取字符串second foo
-
/ipfs/QmUmg7BZC1YP1ca66rRtWKxpXp77WgVHrnv263JtDuvs2k/a/b/foo/name
遍历第一个和最后一个对象并获取字符串third foo
什么是IPLD数据模型?
IPLD数据模型为所有merkle-dag定义了一个简单的基于JSON的结构,并标识了一组格式来将结构编码进去。
限制和欲望
一些限制:
- IPLD路径必须是明确的。给定的路径字符串必须总是确定性地遍历到同一个对象。(例如避免重复链接名称)
- IPLD路径必须是通用的,避免对非英语国家不友好(例如使用UTF-8,而不是ASCII)。
- IPLD路径必须在UNIX和Web上干净地分层(使用/,对ASCII系统具有确定性的变换)。
- 考虑到JSON的广泛成功,大量的系统提供了JSON接口。IPLD必须能够对JSON进行简单的导入和导出。
- JSON数据模型也非常简单易用。IPLD必须一样易于使用。
- 定义新数据结构必须非常简单。要在IPLD上尝试新的定义,不应该很麻烦,也不需要太多的知识。
- 由于IPLD基于JSON数据模型,因此它完全可以通过JSON-LD与RDF和关联数据标准兼容。
- IPLD序列化格式(在磁盘上和在线上)必须快速且节省空间。(不应该使用JSON作为存储格式,而应使用CBOR或类似的格式)
- IPLD密码哈希必须是可升级的(使用multihash)
一些不错的点:
- IPLD不应该犯下错误,例如JSON中缺少整数。
- IPLD应该是可升级的,例如,如果出现更好的磁盘格式,系统应该能够迁移到它之上并且最小化这样做的成本。
- IPLD对象应该能够解析路径的属性,而不仅仅是merkle links。
- IPLD Canonical Format应该易于编写解析器。
- IPLD Canonical Format应该能在不解析完整对象的情况下进行查找。(CBOR和Protobuf允许)。
格式定义
(注意:这里我们将使用JSON和YML来显示格式是什么样的。我们显式地使用这两种方法来显示不同格式的对象的等价性。)
IPLD数据模型“是JSON”,它(a)也是基于树的文档,具有一些基本类型,(b)1:1映射到JSON, (c)用户可以通过JSON本身使用它。它“不是JSON”(a)在某些错误上有所改进,(b)具有高效的序列化表示,(c)实际上并没有指定单一的on-wire格式,因为众所周知,这个世界正在改进。
基本的节点
以下是JSON中的示例IPLD对象:
{
“ name ”:“ Vannevar Bush ”
}
假设它散列的multihash值QmAAA...AAA
。注意,它根本没有链接,只是一个字符串名称值。但是我们仍然能够“解析”它下面的key name:
> ipld cat --json QmAAA...AAA
{
"name": "Vannevar Bush"
}
> ipld cat --json QmAAA...AAA/name
"Vannevar Bush"
当然,我们可以用其他格式来查看它
> ipld cat --yml QmAAA...AAA
---
name: Vannevar Bush
> ipld cat --xml QmAAA...AAA
<!xml> <!-- todo -->
<node>
<name>Vannevar Bush</name>
</node>
链接节点之间
节点之间的Merkle-Linking是IPLD存在的原因。IPLD中的链接只是一种特殊格式的嵌入式节点:
{
"title": "As We May Think",
"author": {
"/": "QmAAA...AAA" // links to the node above.
}
}
假设这个散列值为multihash值QmBBB...BBB
。该节点通过子路径author
链接到QmAAA...AAA
,上面这一节的节点。所以我们现在可以做到:
> ipld cat --json QmBBB...BBB
{
"title": "As We May Think",
"author": {
"/": "QmAAA...AAA" // links to the node above.
}
}
> ipld cat --json QmBBB...BBB/author
{
"name": "Vannevar Bush"
}
> ipld cat --yml QmBBB...BBB/author
---
name: "Vannevar Bush"
> ipld cat --json QmBBB...BBB/author/name
"Vannevar Bush"
链接属性约定
IPLD允许用户构建复杂的数据结构,以及与链接相关的其他属性。这对编码其他信息以及链接(例如关系类型或链接中所需的辅助数据)很有用。这与下面讨论的“链接对象约定”不同,它们本身非常有用。但有时候,你只是想在链接上添加一些数据而不必创建另一个对象。IPLD不会妨碍你。您可以简单地通过将实际的IPLD链接嵌套在另一个对象中,并使用其他属性来完成。
重要提示:链接属性不允许直接在链接对象中使用,因为存在明显的歧义。阅读规格历史,了解有关难点的讨论。
例如,假设您有一个文件系统,并希望在对象之间的链接中分配类似于权限或所有者的元数据。假设你有一个哈希值为QmCCC...CCC
的目录对象像这样:
{
"foo": { // link wrapper with more properties
"link": {"/": "QmCCC...111"} // the link
"mode": "0755",
"owner": "jbenet"
},
"cat.jpg": {
"link": {"/": "QmCCC...222"},
"mode": "0644",
"owner": "jbenet"
},
"doge.jpg": {
"link": {"/": "QmCCC...333"},
"mode": "0644",
"owner": "jbenet"
}
}
或YML
---
foo:
link:
/: QmCCC...111
mode: 0755
owner: jbenet
cat.jpg:
link:
/: QmCCC...222
mode: 0644
owner: jbenet
doge.jpg:
link:
/: QmCCC...333
mode: 0644
owner: jbenet
虽然我们在特定于此数据结构的链接中拥有新属性,但我们仍然可以很好地解析链接:
> ipld cat --json QmCCC...CCC/cat.jpg
{
"data": "\u0008\u0002\u0012��\u0008����\u0000\u0010JFIF\u0000\u0001\u0001\u0001\u0000H\u0000H..."
}
> ipld cat --json QmCCC...CCC/doge.jpg
{
"subfiles": [
{
"/": "QmPHPs1P3JaWi53q5qqiNauPhiTqa3S1mbszcVPHKGNWRh"
},
{
"/": "QmPCuqUTNb21VDqtp5b8VsNzKEMtUsZCCVsEUBrjhERRSR"
},
{
"/": "QmS7zrNSHEt5GpcaKrwdbnv1nckBreUxWnLaV4qivjaNr3"
}
]
}
> ipld cat --yml QmCCC...CCC/doge.jpg
---
subfiles:
- /: QmPHPs1P3JaWi53q5qqiNauPhiTqa3S1mbszcVPHKGNWRh
- /: QmPCuqUTNb21VDqtp5b8VsNzKEMtUsZCCVsEUBrjhERRSR
- /: QmS7zrNSHEt5GpcaKrwdbnv1nckBreUxWnLaV4qivjaNr3
> ipld cat --json QmCCC...CCC/doge.jpg/subfiles/1/
{
"data": "\u0008\u0002\u0012��\u0008����\u0000\u0010JFIF\u0000\u0001\u0001\u0001\u0000H\u0000H..."
}
但是我们无法像其他属性那样很好地提取链接,因为链接是要通过解析的。
重复属性keys
注意,有两个同名的属性是不允许的,但实际上不可能阻止(有人会这样做并将它提供给解析器),所以为了安全起见,我们定义了路径遍历的值作为序列化表示中的第一个条目。例如,假设我们有对象:
{
"name": "J.C.R. Licklider",
"name": "Hans Moravec"
}
假设这是规范格式(不是json,而是cbor)的准确顺序,并且它的散列为QmDDD…DDD
。我们总是得到:
> ipld cat --json QmDDD...DDD
{
"name": "J.C.R. Licklider",
"name": "Hans Moravec"
}
> ipld cat --json QmDDD...DDD/name
"J.C.R. Licklider"
路径限制
Unix和Web中的路径描述有一些重要的问题。有关讨论,请参阅此讨论。为了与unix和web的模型和期望兼容,IPLD明确禁止具有特定路径组件的路径。请注意,数据本身可能仍然包含这些属性(有人会这样做,并且有合法用途)。所以只有路径解析器不能通过这些路径来解析。这些限制与典型的unix和UTF-8路径系统相同:
todo:
- [ ] 列表路径解析限制
- [ ] show示例
JSON中的整型
IPLD可以直接与JSON兼容,以利用JSON的成功,但它不需要因为JSON的错误而受到限制。这是我们可以遵循格式惯用选择的地方,但必须注意确保始终存在定义明确的1:1映射。
关于整数,在JSON中存在多种表示整数为字符串的格式,例如EJSON。这些可以被使用和转换到其他格式,这是自然发生的- 也就是说,将JSON转换为CBOR时,应该将EJSON整数自然地转换为合适的CBOR整数,而不是将其表示为字符串值的映射。
序列化数据格式
IPLD通过multicodec支持各种序列化数据格式。这些可以使用,但是对于格式是惯用的,例如CBOR,我们可以使用CBOR类型tags来表示merkl-link,并避免写出完整的字符串键@link。鼓励用户充分使用这些格式,并以最有意义的任何格式存储和传输IPLD数据。唯一的要求是必须有一个明确定义的IPLD规范格式的一对一映射。这样就可以将数据从一种格式转换为另一种格式,而不必改变其含义或密码哈希值。
带标签的序列化CBOR
在CBOR中,可以使用定义在RFC 7049 section 2.4.中的标记来表示IPLD链接。
标签<tag-link-object>
被定义。这个标签可以是一个文本字符串(主类型3)或者是对应于链接目标的字节字符串(主类型2)。
将IPLD“链接对象”编码到CBOR时,请使用以下算法:
- 提取链接值。
- 如果链接值是一个有效的multiaddress,并且将该链接文本转换为多地址二进制字符串,并返回到文本将保证产生完全相同的文本,则该链接将转换为存储在CBOR中的二进制多地址字节字符串(主类型2)。
- 否则,链接值被存储为文本(主类型3)
- 由此产生的编码是链接值
<tag-link-object>
的CBOR表示
当解码CBOR并将其转换为IPLD时,<tag-link-object>的每一个事件都由以下算法转换:
- 以下值必须是提取的链接值。
- 如果链接是二进制字符串,则将其解释为多地址并转换为文本格式。否则,直接使用文本字符串。
- 用一个键值对创建一个映射。关键是标准的IPLD链接key/,该值是包含链接值的文本字符串。
当一个IPLD对象以这里所述的方式包含这些标记时,用于表示对象编解码器的multicodec头必须是/cbor/ IPLD -tagsv1,而不仅仅是/cbor。读者应该能够使用优化的阅读过程来检测使用这些标签的链接。
规范格式
为了保持merkle-linking的能力,我们必须确保IPLD文档有一个单一的规范序列化表示。这可确保应用程序获得相同的加密哈希。应该指出的是,这是一个系统范围的参数。未来的系统可能会改变它来演进表示。不过,我们估计这需要十年不超过一次。
IPLD规范格式是带tags的规范化CBOR。
除了此处定义的规则外,规范CBOR格式必须遵循RFC 7049 section 3.9中定义的规则。
这种格式的用户不应该期望keys的任何特定排序,因为keys可能以不同的非标准格式排序。
传统的规范格式是protocol buffers。
这种规范格式用于决定首次创建对象并计算其哈希时使用的格式。一旦为IPLD对象决定了格式,它必须在所有通信中使用,以便发送者和接收者可以根据散列来检查数据。
例如,当发送一个在protocol buffers中编码的传统对象时,发送方不得发送CBOR版本,因为接收方将无法检查文件的有效性。
同样,当接收者存储对象时,它必须确保该对象的规范格式与对象一起存储,以便能够与其他节点共享该对象。
用它们的格式存储这些对象的一种简单方法是用它们的multicodec头存储它们。
数据结构示例
重要的是,IPLD是一种简单,灵活和可扩展的格式,不会妨碍用户定义新的或导入旧数据文件的方式。为此,下面我将展示一些示例数据结构。
Unix文件系统
一个小文件
{
“ data ”: “ hello world ”,
“ size ”: “ 11 ”
}
一个分块文件
分割成多个独立的子文件。
{
"size": "1424119",
"subfiles": [
{
"link": {"/": "QmAAA..."},
"size": "100324"
},
{
"link": {"/": "QmAA1..."},
"size": "120345",
"repeat": "10"
},
{
"link": {"/": "QmAA1..."},
"size": "120345"
},
]
}
目录
{
“ foo ”: {
“ link ”: { “ / ”: “ QmCCC ... 111 ” },
“ mode ”: “ 0755 ”,
“ owner ”: “ jbenet ”
},
“ cat.jpg ”: {
“ link ”: { “ / ”: “ QmCCC ... 222 ” },
“ mode ”: “ 0644 ”,
“ owner ”: “ jbenet ”
},
“ doge.jpg ”: {
“ link ”: { “ / ”: “ QmCCC ... 333 ” },
“ mode ”: “ 0644 ”,
“ owner ”: “ jbenet ”
}
}
git
git blob
{
“ data ”: “ hello world ”
}
git tree
{
“ foo ”: {
“ link ”: { “ / ”: “ QmCCC ... 111 ” },
“ mode ”: “ 0755 ”
},
“ cat.jpg ”: {
“ link ”: { “ / ”: “ QmCCC ... 222 ” },
“ mode ”: “ 0644 ”
},
“ doge.jpg ”: {
“ link ”: { “ / ”: “ QmCCC ... 333 ” },
“ mode ”: “ 0644 ”
}
}
git commit
{
"tree": {"/": "e4647147e940e2fab134e7f3d8a40c2022cb36f3"},
"parents": [
{"/": "b7d3ead1d80086940409206f5bd1a7a858ab6c95"},
{"/": "ba8fbf7bc07818fa2892bd1a302081214b452afb"}
],
"author": {
"name": "Juan Batiz-Benet",
"email": "juan@benet.ai",
"time": "1435398707 -0700"
},
"committer": {
"name": "Juan Batiz-Benet",
"email": "juan@benet.ai",
"time": "1435398707 -0700"
},
"message": "Merge pull request #7 from ipfs/iprs\n\n(WIP) records + merkledag specs"
}
比特币
比特币block
{
“ parent ”: { “ / ”: “ Qm000000002CPGAzmfdYPghgrFtYFB6pf1BqMvqfiPDam8 ” },
“ transactions ”: { “ / ”: “ QmTgzctfxxE8ZwBNGn744rL5R826EtZWzKvv2TF2dAcd9n ” },
“ nonce ”: “ UJPTFZnR2CPGAzmfdYPghgrFtYFB6pf1BqMvqfiPDam8 ”
}
比特币交易
这一次,在YML中。TODO:让它是一个真正的txn
---
inputs:
- input: {/: Qmes5e1x9YEku2Y4kDgT6pjf91TPGsE2nJAaAKgwnUqR82}
amount: 100
outputs:
- output: {/: Qmes5e1x9YEku2Y4kDgT6pjf91TPGsE2nJAaAKgwnUqR82}
amount: 50
- output: {/: QmbcfRVZqMNVRcarRN3JjEJCHhQBcUeqzZfa3zoWMaSrTW}
amount: 30
- output: {/: QmV9PkR2gXcmUgNH7s7zMg9dsk7Hy7bLS18S9SHK96m7zV}
amount: 15
- output: {/: QmP8r8fLUnEywGnRRUrHB28nnBKwmshMLiYeg8udzYg7TK}
amount: 5
script: OP_VERIFY