- ipld.io
- Github:ipld
- 原文:IPLD specs
有許多系統使用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: jbenetcat.jpg: link: /: QmCCC...222 mode: 0644 owner: jbenetdoge.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:
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: 100outputs: - output: {/: Qmes5e1x9YEku2Y4kDgT6pjf91TPGsE2nJAaAKgwnUqR82} amount: 50 - output: {/: QmbcfRVZqMNVRcarRN3JjEJCHhQBcUeqzZfa3zoWMaSrTW} amount: 30 - output: {/: QmV9PkR2gXcmUgNH7s7zMg9dsk7Hy7bLS18S9SHK96m7zV} amount: 15 - output: {/: QmP8r8fLUnEywGnRRUrHB28nnBKwmshMLiYeg8udzYg7TK} amount: 5script: OP_VERIFY