IPFS - 內(nèi)容尋址的版本化點對點文件系統(tǒng)(草稿3) 翻譯

IPFS - Content Addressed, Versioned, P2P File System (draft 3)

摘要

星際文件系統(tǒng)是一個點對點分布式文件系統(tǒng), 旨在使用相同的文件系統(tǒng)連接所有的計算設(shè)備。在某種情形下,IPFS很類似Web,不過IPFS可以被看作一個獨立的使用Git倉庫來交換對象的BitTorrent集群。換句話說,IPFS提供了一個高吞吐量的以內(nèi)容可尋址的塊存儲模型,具有內(nèi)容尋址的超鏈接。這便形成了廣義的Merkle DAG數(shù)據(jù)結(jié)構(gòu),這種數(shù)據(jù)結(jié)構(gòu)可以構(gòu)建成一個文件版本系統(tǒng),區(qū)塊鏈,甚至一個永久性Web。IPFS使用分布式哈希表,激勵化的塊交易模型和自我認證的命名空間。IPFS沒有單獨的故障節(jié)點,節(jié)點之間不需要完全信任基礎(chǔ)。

1.介紹

關(guān)于構(gòu)建全球分布式文件系統(tǒng),已經(jīng)有許多嘗試。一些系統(tǒng)已經(jīng)取得了重大的成功, 而一些卻完全失敗了。在學(xué)術(shù)嘗試中, AFS[6] 就是成功的例子,并且得到廣泛的應(yīng)用。 然而,其他的[7, ?] 卻沒有成功。學(xué)術(shù)界之外,最成功的系統(tǒng)是面向音視頻媒體的點對點文件共享系統(tǒng)。 最值得注意的是, Napster, KaZaA 和BitTorrent[2] 部署的文件分發(fā)系統(tǒng)可以支持1億用戶同時在線。即使今天, BitTorrent 也維持著每天千萬節(jié)點的活躍數(shù)[16]。
相比學(xué)術(shù)文件系統(tǒng),這些應(yīng)用獲得了更多的用戶和文件分發(fā),然而這些應(yīng)用并沒有被設(shè)計為基礎(chǔ)設(shè)施。雖然這些應(yīng)用已經(jīng)取得了成功的回報,但目前還沒有出現(xiàn)為全球提供低延遲和分散式分發(fā)的廣義的文件系統(tǒng)。
也許是因為HTTP這樣“足夠好“的系統(tǒng)已經(jīng)存在。到目前為止,HTTP作為最成功的“分布式文件系統(tǒng)“的協(xié)議已經(jīng)大量部署,再與瀏覽器相結(jié)合,具有巨大的技術(shù)和社會影響力。它已經(jīng)成為互聯(lián)網(wǎng)傳輸文件的事實標準。然而,他沒有采用最近15年的發(fā)明的數(shù)十種先進的文件分發(fā)技術(shù)。 從一方面講, 由于向后兼容的限制和當前模式的強烈投入, 進化目前的Web基礎(chǔ)架構(gòu)幾乎不可能。但從另一個角度看,自從出現(xiàn)了HTTP,新的協(xié)議已經(jīng)出現(xiàn)并被廣泛使用。目前的難題缺是升級設(shè)計:不會降低用戶體驗情況下引入新功能,以增強目前的HTTP Web。
長期使用HTTP的業(yè)界已經(jīng)消失了,因為移動小文件是非常便宜,即使是具有大量流量的小型組織。但是,隨著新的挑戰(zhàn),我們正在進入數(shù)據(jù)分發(fā)的新時代:

  • (a)托管和分發(fā)PB級數(shù)據(jù)集
  • (b)計算跨組織的大數(shù)據(jù)
  • (c)按需量或?qū)崟r媒體流量大規(guī)模高清晰度定義
  • (d)大規(guī)模數(shù)據(jù)集的版本化和鏈接
  • (e)防止重要文件的意外消失等。

許多這些可以歸結(jié)為“大量數(shù)據(jù),無處不在”。由于關(guān)鍵功能和帶寬問題,我們已經(jīng)放棄了不同數(shù)據(jù)的HTTP分發(fā)協(xié)議。下一步是使它們成為一部分的Web本身。

2.背景

本節(jié)回顧了IPFS所采用的點對點系統(tǒng)的重要技術(shù)特性。

2.1 分布式哈希表

分布式哈希表(DHTs)廣泛的使用于定位和維持點對點系統(tǒng)的元數(shù)據(jù)。例如BitTorrent的MainlineDHT技術(shù)跟蹤torrent集群的對等節(jié)點。

2.1.1 Kademlia DHT

Kademlia[10] 是一個廣泛流行的DHT,提供了一下特點:

  1. 大規(guī)模網(wǎng)絡(luò)下高效查詢:查詢平均訪問O(log2N)節(jié)點。(例如,1000萬節(jié)點的網(wǎng)絡(luò)需要20跳查詢)
  2. 低協(xié)調(diào)開銷:優(yōu)化控制消息發(fā)送到其他節(jié)點的數(shù)量。
  3. 使用長時間在線節(jié)點來抵抗各種攻擊。
  4. 廣泛使用于點對點應(yīng)用中,包括Gnutella和BitTorrent,形成了超過2000萬個節(jié)點的網(wǎng)絡(luò)[16]。

2.1.2 Coral DSHT

雖然一些對等文件系統(tǒng)直接在DHT中存儲數(shù)據(jù)塊,“不應(yīng)該存儲在節(jié)點的數(shù)據(jù)存儲在節(jié)點會浪費存儲和帶寬”[5]。Coral DSHT通過以下三種方式擴展了Kademlia:

  1. Kademlia將值存儲在距離其key最接近(使用XOR-distance)的節(jié)點中。這不考慮應(yīng)用數(shù)據(jù)的局部性,忽略“遠處”可能已經(jīng)擁有此數(shù)據(jù)的節(jié)點,并強制“最近”節(jié)點存儲它,無論它們是否需要。這將浪費了大量的存儲和帶寬。相反, Coral 存儲了地址, 該地址的對等節(jié)點可以提供相應(yīng)的數(shù)據(jù)塊。
  2. Coral將DHT API的get_value(key)換成了get_any_values(key)(DSHT中的“sloppy”)。這將依舊工作,是因為Coral用戶只需要一個(工作)的對等節(jié)點,而不是完整的列表。作為回報,Coral可以僅將子集分配到“最近”的節(jié)點,避免熱點(當密鑰變得流行時,重載所有最近的節(jié)點)。
  3. 另外,Coral根據(jù)區(qū)域和大小組織了一個稱為clusters的獨立DSHT層次結(jié)構(gòu)。這使得節(jié)點首先查詢其區(qū)域中的對等體,“查找附近的數(shù)據(jù)而不查詢遠程節(jié)點”[5]并大大減少查找的延遲。

2.1.3 S/Kademlia DHT

S/Kademlia[1]使用以下方式擴展了Kademlia來防止惡意的攻擊:

  1. S/Kad 提供了方案來保證NodeId的生成,防止Sybill攻擊。它需要節(jié)點產(chǎn)生PKI公私鑰對,從中獲得他們的Identity,并彼此間簽名。一個方案使用POW工作量證明,使得生成Sybills攻擊的成本高昂。
  2. S/Kad 節(jié)點在不相交的路徑上查找, 即使網(wǎng)絡(luò)中存在大量的不誠實節(jié)點,也能確保誠實節(jié)點可以互相鏈接。即使網(wǎng)絡(luò)中存在一半的不誠實節(jié)點,S/Kad 也能達到85%的成功率。

2.2塊交換 - BitTorrent

BitTorrent[3] 是一個非常成功的點對點共享文件系統(tǒng),它可以在存在不信任的對等節(jié)點(群集)的協(xié)作網(wǎng)絡(luò)中分發(fā)各自的文件數(shù)據(jù)片。從BitTorrent和它的生態(tài)系統(tǒng)的關(guān)鍵特征中 IPFS得到啟示如下:

  1. BitTorrent的數(shù)據(jù)交換協(xié)議使用了一種bit-for-tat的激勵策略, 可以獎勵對其他節(jié)點做出貢獻的節(jié)點,懲罰只吸取對方資源的節(jié)點
  2. BitTorrent對等節(jié)點跟蹤文件的可用性,優(yōu)先發(fā)送稀有片段。這減輕了seeds節(jié)點的負擔(dān), 讓non-seeds節(jié)點有能力互相交易。
  3. 對于一些剝削帶寬共享策略, BitTorrent的標準tit-for-tat策略是非常脆弱的。 然而,PropShare[8]是一種不同的對等節(jié)點帶寬分配策略, 可以更好的抵制剝削戰(zhàn)略, 提高群集的表現(xiàn)。

2.3版本控制系統(tǒng) - Git

版本控制系統(tǒng)提供了對隨時間變化的文件進行建模的設(shè)施,并有效地分發(fā)不同的版本。流行版本控制系統(tǒng)Git提供了強大的Merkle DAG對象模型,以分布式友好的方式捕獲對文件系統(tǒng)樹的更改。

  1. 不可更改的對象表示文件(blob),目錄(tree)和更改(commit)。
  2. 對內(nèi)容加密哈希,讓對象可尋址。
  3. links到其他對象是嵌入的,形成一個Merkle DAG。這提供了很多有用的完整和work-flow屬性。
  4. 很多版本元數(shù)據(jù)(分支,標示等)都只是指針引用,因此創(chuàng)建和更新的代價都小。
  5. 版本改變只是更新引用或者添加對象。
  6. 分布式版本改變對其他用戶而言只是轉(zhuǎn)移對象和更新遠程引用。

2.4自驗證文件系統(tǒng) - SFS

SFS[12,11]提出了兩個引人注目的實現(xiàn)(a)分布式信任鏈,和(b)平等共享的全局命名空間。SFS引入了一種建構(gòu)自我認證文件系統(tǒng)的技術(shù):使用以下格式/sfs/<Location>:<HostID>尋址遠程文件系統(tǒng),Location是服務(wù)器網(wǎng)絡(luò)地址,并且: HostID = hash(public_key || Location)
因此SFS文件系統(tǒng)的名字認證了它的服務(wù),用戶可以通過服務(wù)提供的公鑰來驗證,協(xié)商一個共享的私鑰,保證所有的通信。所有的SFS實例都共享了一個全局的命名空間,這個命名空間的名稱分配是加密的,不被任何中心化的body控制。

3.IPFS設(shè)計

IPFS是一個對等系統(tǒng),沒有節(jié)點擁有特權(quán)。IPFS節(jié)點在本地存儲IPFS對象。節(jié)點之間彼此連接并且傳送對象。IPFS協(xié)議根據(jù)不同的功能劃分成以下子協(xié)議棧:

  1. 身份 - 管理節(jié)點的創(chuàng)建和認證。
  2. 網(wǎng)絡(luò) - 通過可配置化的不同的底層網(wǎng)絡(luò)協(xié)議,管理節(jié)點之間連接。
  3. 路由 - 維護定位特定對等節(jié)點和對象的信息表。應(yīng)對本地和遠端查詢。默認是DHT,可以替換。
  4. 交換 - 一個新奇的塊交換協(xié)議(BitSwap),用來管理塊分發(fā)效率, 模擬市場, 輕微刺激數(shù)據(jù)復(fù)制。交易策略可以替換。
  5. 對象 - 一個對應(yīng)鏈接的以內(nèi)容尋址的不可變的Merkle DAG對象??梢员硎救我鈹?shù)據(jù)結(jié)構(gòu),例如,文件層級結(jié)構(gòu)和通信系統(tǒng)。
  6. 文件 - 受Git啟發(fā)的文件版本層級系統(tǒng)
  7. 命名 - 自我認證的可變命名系統(tǒng)

這些子系統(tǒng)不是獨立的;它們集成在一起,特性互相使用。然而分開描述他們依然是有用的,構(gòu)建自底而上的協(xié)議棧。
符號:以下數(shù)據(jù)結(jié)構(gòu)和方法都是Go語言語法。

3.1 身份

節(jié)點被NodeId標識,是通過S/Kademlia’s的靜態(tài)加密難題[1]創(chuàng)建的公鑰的加密散列。節(jié)點存儲他們的公私鑰(密碼加密)。用戶可以在每次啟動時都實例一個新的節(jié)點標識,不過會丟失之前擁有的網(wǎng)絡(luò)利益。節(jié)點激勵措施保持不變。

type NodeId Multihash
type Multihash []byte
//self-describing cryptographic hash digest
type PublicKey []byte
type PrivateKey []byte
//self-describing keys

type Node struct {
    NodeId NodeID
    PubKey PublickKey
    PriKey PrivateKey
}

基于S/Kademlia創(chuàng)建IPFS身份標識:

difficulty = <integer parameter>
n = Node{}
do{
    n.PubKey, n.PrivaKey = PKI.genKeyPair()
    n.NodeId = hash(n.PubKey)
    p = count_preceding_zero_bits(hash(n.NodeId)
} while (p < difficulty)

第一次建立連接時,對等節(jié)點之間交換公鑰,校驗:hash(other.PublicKey) equals other.NodeId。如果不相等,連接中斷。

加密函數(shù)須知

相對于將系統(tǒng)鎖定在一些特殊的方法集,IPFS更傾向自我描述值。哈希摘要值以multihash形式存儲,其頭部包括使用的哈希函數(shù)說明和以字節(jié)為單位的摘要長度。例如:

<function code><digest length><digest bytes>

這允許系統(tǒng)來(a)選擇最佳函數(shù)(安全性和速度性能) (b)隨著方法選項的更新而進化,自我描述值允許使用不同參數(shù)選項兼容。

3.2 網(wǎng)絡(luò)

IPFS節(jié)點通過潛在的廣義網(wǎng)絡(luò)和IPFS網(wǎng)絡(luò)中的上百個節(jié)點定期通信。IPFS網(wǎng)絡(luò)棧特性:

  • 傳輸層: IPFS可以使用任何傳輸層協(xié)議,最好符合WebRTC DataChannels?或者uTP(LEDBAT [14])。
  • 可靠性: IPFS所依賴的底層網(wǎng)絡(luò)不能保證的可靠性的話,便使用uTP (LEDBAT [14])或者SCTP [15]來保證可靠性。
  • 連通性: IPFS同樣使用ICE NAT遍歷技術(shù)[13]。
  • 完整性: 提供使用哈希校驗和來檢驗消息的完整性
  • 確定性: 提供使用發(fā)送者公鑰的HMAC來核查消息的真實性

3.2.1 對等網(wǎng)絡(luò)地址須知

IPFS可以使用任意網(wǎng)絡(luò)連接。他不依賴IP并且不假設(shè)已經(jīng)獲取IP。這使得IPFS可以在覆蓋網(wǎng)絡(luò)中使用。IPFS以multiaddr字節(jié)字符串形式保存地址,以供給底層網(wǎng)絡(luò)使用。multiaddr提供了一種地址和協(xié)議的描述方式,并且提供了封裝格式。例如:

# an SCTP/IPv4 connection
/ip4/10.20.30.40/sctp/1234/
# an SCTP/IPv4 connection proxied over TCP/IPv4
/ip4/5.6.7.8/tcp/5678/ip4/1.2.3.4/sctp/1234/

3.3 路由

IPFS節(jié)點需要一個路由系統(tǒng)來(a)尋找別的節(jié)點的網(wǎng)絡(luò)地址,(b)尋找節(jié)點的服務(wù)對象。IPFS通過使用一個基于S/Kademlia和Coral的DSHT來解決上述問題,具體的特性詳見2.1。對象的大小和IPFS使用的模式方面,類似于Coral[5]和Mainline[16],因此IPFS的DHT根據(jù)對象存儲時的大小對其進行區(qū)分。小的值(小于等于1KB)直接存儲在DHT中。對于大的值,DHT中存儲的是具體存儲不同區(qū)塊的對等節(jié)點的NodeIds的引用。
DSHT的接口如下:

type IPFSRouting interface {
    FindPeer(node NodeId)
    // gets a particular peer’s network address
    SetValue(key []bytes, value []bytes)
    // stores a small metadata value in DHT
    GetValue(key []bytes)
    // retrieves small metadata value from DHT
    ProvideValue(key Multihash)
    // announces this node can serve a large value
    FindValuePeers(key Multihash, min int)
    // gets a number of peers serving a large value

注意: 不用的路由系統(tǒng)中需要不同的路由實例(廣泛網(wǎng)絡(luò)中使用DHT, 局域網(wǎng)絡(luò)中使用靜態(tài)HT)。因此IPFS路由系統(tǒng)可以根據(jù)用戶需求來替換。只要接口保持匹配,系統(tǒng)便能正常運行。

3.4 塊交換 - BitSwap協(xié)議

受到BitTorrent 的啟發(fā),IPFS 中的BitSwap協(xié)議通過對等節(jié)點間交換數(shù)據(jù)塊來分發(fā)數(shù)據(jù)的。像BitTorrent一樣, 對等節(jié)點在換取想要的塊數(shù)據(jù)是以交換自己所擁有的塊數(shù)據(jù)為前提的。和BitTorrent協(xié)議不同的是, BitSwap不局限于一個torrent文件中的數(shù)據(jù)塊。BitSwap 協(xié)議中存在一個永久的市場。 這個市場包括各個節(jié)點可以獲取的所有塊數(shù)據(jù),而不管在意這些塊是哪些文件中的一部分。這些快數(shù)據(jù)可能來自文件系統(tǒng)中完全不相關(guān)的文件。 這個市場是由所有的節(jié)點組成的。
雖然易貨系統(tǒng)的概念意味著可以創(chuàng)建虛擬貨幣,但這將需要一個全局分類賬本來跟蹤貨幣的所有權(quán)和轉(zhuǎn)移。這可以實施為BitSwap策略,并將在未來的論文中探討。
在基本情況下,BitSwap節(jié)點必須以塊形式彼此提供直接的值。只有當跨節(jié)點的塊的分布是互補的時候,各取所需的時候,這才會工作的很好。 通常情況并非如此,在某些情況下,節(jié)點必須為自己的塊而工作。 在節(jié)點沒有其對等節(jié)點所需的(或根本沒有的)情況下,它會更低的優(yōu)先級去尋找對等節(jié)點想要的塊。這會激勵節(jié)點去緩存和傳播稀有片段, 即使節(jié)點對這些片段不感興趣。

3.4.1 BitSwap信用

這個協(xié)議必須帶有激勵機制, 去激勵節(jié)點去seed 其他節(jié)點所需要的塊,而它們本身是不需要這些塊的。 因此, BitSwap的節(jié)點很積極去給對端節(jié)點發(fā)送塊,期待獲得報酬。但必須防止水蛭攻擊(空負載節(jié)點從不共享塊),一個簡單的類似信用的系統(tǒng)解決了這些問題:

  1. 對等節(jié)點間會追蹤他們的平衡(通過字節(jié)認證的方式)。
  2. 隨著債務(wù)增加而概率降低,對等者概率的向債務(wù)人發(fā)送塊。

如果節(jié)點決定不發(fā)送到對等節(jié)點,節(jié)點隨后忽略對等體的ignore_cooldown超時。 這樣可以防止發(fā)送者嘗試多次發(fā)送(洪水攻擊) (BitSwap默認是10秒)。

3.4.2 BitSwap策略

BitSwap 對等節(jié)點采用很多不同的策略,這些策略對整個數(shù)據(jù)塊的交換執(zhí)行力產(chǎn)生了不同的巨大影響。在BitTorrent 中, 標準策略是特定的(tit-for-tat),從BitTyrant [8](盡可能分享)到BitThief [8](利用一個漏洞,從不共享),到PropShare [8](按比例分享),其他不同的策略也已經(jīng)被實施。BitSwap 對等節(jié)點可以類似地實現(xiàn)一系列的策略(良好和惡意)。對于功能的選擇,應(yīng)該明確:

  1. 為整個交易和節(jié)點最大化交易成績。
  2. 防止空負載節(jié)點利用和損害交易。
  3. 高效抵制未知策略
  4. 對可信任的對等節(jié)點更寬容。

探索這些策略的空白是未來的事情。在實踐中使用的一個選擇性功能是sigmoid,根據(jù)負債比例進行縮放:
讓負債比例在一個節(jié)點和它對等節(jié)點之間:
r = bytes_sent / bytes_recv + 1根據(jù)r,發(fā)送到負債節(jié)點的概率為:P(send | r ) = 1 ? ( 1/ ( 1 + exp(6 ? 3r) ) )
從圖片1中看到,當節(jié)點負債比例超過節(jié)點已建立信貸的兩倍,發(fā)送到負債節(jié)點的概率就會急速下降。
負債比是信任的衡量標準:對于之前成功的互換過很多數(shù)據(jù)的節(jié)點會寬容債務(wù),而對不信任不了解的節(jié)點會嚴格很多。這個(a)給與那些創(chuàng)造很多節(jié)點的攻擊者(sybill 攻擊)一個障礙。(b)保護了之前成功交易節(jié)點之間的關(guān)系,即使這個節(jié)點暫時無法提供數(shù)據(jù)。(c)最終阻塞那些關(guān)系已經(jīng)惡化的節(jié)點之間的通信,直到他們被再次證明。

3.4.3 BitSwap賬本

BitSwap節(jié)點保存了一個記錄與所有其他節(jié)點之間交易的賬本。這個可以讓節(jié)點追蹤歷史記錄以及避免被篡改。當激活了一個鏈接,BitSwap節(jié)點就會互換它們賬本信息。如果這些賬本信息并不完全相同,分類賬本將會重新初始化, 那些應(yīng)計信貸和債務(wù)會丟失。 惡意節(jié)點會有意去失去“這些“賬本, 從而期望清除自己的債務(wù)。節(jié)點是不太可能在失去了應(yīng)計信托的情況下還能累積足夠的債務(wù)去授權(quán)認證。伙伴節(jié)點可以自由的將其視為不當行為, 拒絕交易。

type Ledger struct {
    owner NodeId
    partner NodeId
    bytes_sent int
    bytes_recv int
    timestamp Timestamp
}

節(jié)點可以自由的保留分布式賬本歷史,這不需要正確的操作,因為只有當前的分類賬本條目是有用的。節(jié)點也可以根據(jù)需要自由收集分布式帳本,從不太有用的分布式帳開始:老(其他對等節(jié)點可能不存在)和小。

3.4.4 BitSwap設(shè)計詳述

BitSwap 節(jié)點有以下簡單的協(xié)議。

// Additional state kept
type BitSwap struct {
    ledgers map[NodeId]Ledger
    // Ledgers known to this node, inc inactive
    active map[NodeId]Peer
    // currently open connections to other nodes
    need_list []Multihash
    // checksums of blocks this node needs
    have_list []Multihash
    // checksums of blocks this node has
}
type Peer struct {
    nodeid NodeId
    ledger Ledger
    // Ledger between the node and this peer
    last_seen Timestamp
    // timestamp of last received message
    want_list []Multihash
    // checksums of all blocks wanted by peer
    // includes blocks wanted by peer’s peers
}
// Protocol interface:
interface Peer {
    open (nodeid :NodeId, ledger :Ledger);
    send_want_list (want_list :WantList);
    send_block (block :Block) -> (complete :Bool);
    close (final :Bool);
}

對等連接的生命周期草圖:

  1. Open: 對等節(jié)點間發(fā)送ledgers 直到他們同意。
  2. Sending: 對等節(jié)點間交換want_lists 和blocks。
  3. Close: 對等節(jié)點斷開鏈接。
  4. Ignored: (特殊)對等被忽略(等待時間的超時)如果節(jié)點采用防止發(fā)送策略。

Peer.open(NodeId, Ledger).

當發(fā)生鏈接的時候,節(jié)點會初始化鏈接的賬本,要么保存一個份鏈接過去的賬本,要么創(chuàng)建一個新的被清零的賬本。然后,發(fā)送一個攜帶賬本的open信息給對等節(jié)點。
接收到一個open信息之后,對等節(jié)點可以選擇是否接受此鏈接。如果,根據(jù)接收者的賬本,發(fā)送者是一個不可信的代理(傳輸?shù)陀诹慊蛘哂泻艽蟮奈磧斶€的債務(wù)),接收者可能會選擇忽略這個請求。忽略請求是ignore_cooldown超時來概率性實現(xiàn)的,為了讓錯誤能夠有時間改正和攻擊者被挫敗。
如果鏈接成功,接收者用本地賬本來初始化一個Peer對象以及設(shè)置last_seen時間戳。然后,它會將接受到的賬本與自己的賬本進行比較。如果兩個賬本完全一樣,那么這個鏈接就被Open,如果賬本并不完全一致,那么此節(jié)點會創(chuàng)建一個新的被清零的賬本并且會發(fā)送此賬本。

Peer.send_want_list(WantList)

當鏈接已經(jīng)Open的時候,節(jié)點會廣發(fā)它們的want_list給所有已經(jīng)鏈接的對等節(jié)點。這個是在(a)open鏈接后(b)隨機間歇超時后(c)want_list改變后(d)接收到一個新的塊之后完成的。
當接收到一個want_list之后,節(jié)點會存儲它。然后,會檢查自己是否擁有任何它想要的塊。如果有,會根據(jù)上面提到的BitSwap策略來將want_list所需要的塊發(fā)送出去。

Peer.send_block(Block)

發(fā)送一個塊是直接了當?shù)摹9?jié)點只是傳輸數(shù)據(jù)塊。當接收到了所有數(shù)據(jù)的時候,接收者會計算多重hash校驗和來驗證它是否是自己所需數(shù)據(jù),然后發(fā)送確認信息。
在完成一個正確的塊傳輸之后,接受者會將此塊從need_list一到have_list,最后接收者和發(fā)送者都會更新它們的賬本來反映出傳輸?shù)念~外數(shù)據(jù)字節(jié)數(shù)。
如果一個傳輸驗證失敗了,發(fā)送者要么會出故障要么會攻擊接收者,接收者可以選擇拒絕后面的交易。注意,BitSwap是期望能夠在一個可靠的傳輸通道上進行操作的,所以傳輸錯誤(可能會引起一個對誠實發(fā)送者錯誤的懲罰)是期望在數(shù)據(jù)發(fā)送給BitSwap之前能夠被捕捉到。

Peer.close(Bool)

傳給close最后的一個參數(shù),代表close鏈接是否是發(fā)送者的意愿。如果參數(shù)值為false,接收者可能會立即重新open鏈接,這避免鏈過早的close鏈接。
一個對等節(jié)點close鏈接發(fā)生在下面兩種情況下:
silence_wait超時已經(jīng)過期,并且沒有接收到來自于對等節(jié)點的任何信息(BitSwap默認使用30秒),節(jié)點會發(fā)送Peer.close(false)。
在節(jié)點退出和BitSwap關(guān)閉的時候,節(jié)點會發(fā)送Peer.close(true).
接收到close消息之后,接收者和發(fā)送者會斷開鏈接,清除所有被存儲的狀態(tài)。賬本可能會被保存下來為了以后的便利,當然,只有在被認為賬本以后會有用時才會被保存下來。

注意:
非open信息在一個不活躍的連接上應(yīng)該是被忽略的。在發(fā)送send_block信息時,接收者應(yīng)該檢查這個塊,看它是否是自己所需的,并且是否是正確的,如果是,就使用此塊。總之,所有不規(guī)則的信息都會讓接收者觸發(fā)一個close(false)信息并且強制性的重初始化此鏈接。

3.5 Merkle DAG對象

DHT和BitSwap允許IPFS形成一個大規(guī)模的對等網(wǎng)絡(luò)系統(tǒng)來快速并且健壯的存儲和發(fā)布塊文件。在這些之上, IPFS構(gòu)建了一個Merkle DAG: 一個有向無環(huán)圖,其中對象之間的鏈接是嵌入在源中的目標的加密散列。這是Git數(shù)據(jù)結(jié)構(gòu)的泛化應(yīng)用。MerKle DAGs為IFPS提供了許多有用的特性,包括:

  1. 內(nèi)容尋址: 所有的內(nèi)容都通過它的multihash校驗和唯一標識,包括links。
  2. 防止篡改: 所有的內(nèi)容通過它的校驗和驗證。如果數(shù)據(jù)被篡改或者損壞,IPFS會探測出。
  3. 重復(fù)刪除: 所有的對象內(nèi)容完全相同便會只存儲一份。對于索引對象尤其有用。例如git的tree和commit對象,以及數(shù)據(jù)的共同部分。

IFPS對象的格式如下:

type IPFSLink struct {
    Name string
    // name or alias of this link
    Hash Multihash
    // cryptographic hash of target
    Size int
    // total size of target
}
type IPFSObject struct {
    links []IPFSLink
    // array of links
    data []byte
    // opaque content data
}

IPFS的Merkle DAG是存儲數(shù)據(jù)的一種非常靈活的方式。唯一的要求是對象的引用是(a)內(nèi)容可尋址, (b)以之前描述的格式編碼。IPFS給予應(yīng)用對于data字段的完全控制權(quán)。應(yīng)用可以定制任何數(shù)據(jù)格式,即使IPFS不能夠理解。獨立的內(nèi)部對象link表允許IPFS:

  • 列出一個對象列表中所有的對象引用,比如:

    > ipfs ls /XLZ1625Jjn7SubMDgEyeaynFuR84ginqvzb
    XLYkgq61DYaQ8NhkcqyU7rLcnSa7dSHQ16x 189458 less  
    XLHBNmRQ5sJJrdMPuu48pzeyTtRo39tNDR5 19441 script
    XLF4hwVHsVuZ78FZK6fozf8Jj9WEURMbCX4 5286 template
    <object multihash> <object size> <link name>
    
  • 解決字符串路徑查詢,比如foo/bar/baz。給定一個對象,IPFS將第一個路徑部分映射成對象link表里面的一個hash,獲取第二部分對象。然后重復(fù)至下一個路徑部分。因此任何數(shù)據(jù)格式的字符串路徑都可以通過Merkle DAG定位位置。

  • 解析遞歸引用的所有對象:

    > ipfs refs --recursive \
    /XLZ1625Jjn7SubMDgEyeaynFuR84ginqvzb
    XLLxhdgJcXzLbtsLRL1twCHA2NrURp4H38s
    XLYkgq61DYaQ8NhkcqyU7rLcnSa7dSHQ16x
    XLHBNmRQ5sJJrdMPuu48pzeyTtRo39tNDR5
    XLWVQDqxo9Km9zLyquoC9gAP8CL1gWnHZ7z
    ...
    

一個原始data字段和通用link結(jié)構(gòu)是構(gòu)建IPFS頂層中的任意數(shù)據(jù)結(jié)構(gòu)的必要組件。
通過觀察如下數(shù)據(jù)結(jié)構(gòu): (a) 鍵值對存儲, (b)傳統(tǒng)關(guān)系型數(shù)據(jù), (c)關(guān)聯(lián)數(shù)據(jù)三層存儲, (d) 關(guān)聯(lián)文檔發(fā)布系統(tǒng), (e)通信平臺, (f)加密貨幣區(qū)塊, 可以容易的得知Git的對象模型是如何使用DAG。這些都可以通過IPFS的Merkle DAG建模,使得一些更復(fù)雜的系統(tǒng)使用IPFS系統(tǒng)作為傳輸層協(xié)議。

3.5.1路徑

通過字符串路徑API可以遍歷IPFS對象。路徑的工作方式與傳統(tǒng)的UNIX文件系統(tǒng)和Web一致。Merkle DAG links使得訪問很容易。IPFS完整路徑格式如下:

# format
/ipfs/<hash-of-object>/<name-path-to-object>
# example
/ipfs/XLYkgq61DYaQ8NhkcqyU7rLcnSa7dSHQ16x/foo.txt

/ipfs前綴允許掛載到現(xiàn)在系統(tǒng)中的一個無沖的標準掛載點上(掛載點的名稱是可以配置的)。路徑第二個部分是對象的hash。通常情況下,沒有一個全局跟路徑。一個根對象需要來處理分布式系統(tǒng)中的百萬個對象的一致性,這是一個不可能的任務(wù)。因此,我們模擬通過內(nèi)容地址來模擬根地址。所有的對象總是可以通過他們的hash訪問到。意味著給定地址<foo>/bar/baz的三個對象, 最后一個對象可以通過以下方式獲取:

/ipfs/<hash-of-foo>/bar/baz
/ipfs/<hash-of-bar>/baz
/ipfs/<hash-of-baz>

3.5.2本地對象

IPFS客戶端需要local storage, 一個外部系統(tǒng)用來存儲并且檢索本地原生數(shù)據(jù),以便管理IPFS對象。存數(shù)的類型取決于節(jié)點的使用方式。大多數(shù)情況下,僅僅是硬盤空間的分配(或者通過文件系統(tǒng)管理,或者通過類似leveldb的鍵值存儲,或者直接通過IPFS客戶端)。其他情況下,比如非持久的緩存場景中,存儲需要RAM的分配。
IPFS中所有的塊都在節(jié)點的local storage。當用戶獲取對象時,對象被查到到然后下載最后存儲在本地,至少是臨時存儲。這為一些可配置時間量提供了快速查詢。

3.5.3對象固定

節(jié)點可以通過固定對象的方式來確保特定對象的生存。這樣確保了此對象存儲在節(jié)點的local storage中。固定操作可以使遞歸的,繼而將所有關(guān)聯(lián)的后代對象固定。所有的對象都在本地存儲。在持久化文件中很有用處,包括引用。同樣使得IPFS成為一個連接是永久的Web,并且對象可以確保其他被指定對象的生存。

3.5.4對象發(fā)布

IPFS是全球分發(fā)的。它被設(shè)計為允許用戶成千上萬的文件共同存在。通過內(nèi)容哈希尋址,DHT可以在一種公平安全, 完全分布式的方式發(fā)布對象。任何人都可以通過簡單的向DHT中添加對象的鍵來發(fā)布對象, 并且以對象是對等的方式添加, 別人通過這個對象的路徑來訪問。對象本質(zhì)上是不可改變的, 就像Git。新的版本的hash是不同的, 意味著是新的對象。跟蹤版本是額外版本對象的一個工作。

3.5.5對象級別加密

IPFS擁有處理對象級別加密的操作,一個加密或者簽名的對象是被包裹在一個特殊的frame中,這個frame允許加密或者驗證原生數(shù)據(jù)

type EncryptedObject struct {
    Object []bytes
    // raw object data encrypted
    Tag []bytes
    // optional tag for encryption groups
}
type SignedObject struct {
    Object []bytes
    // raw object data signed
    Signature []bytes
    // hmac signature
    PublicKey []multihash
    // multihash identifying key
}

加密操作改變了對象的hash,被定義為一個新的對象。IPFS自動驗證簽名并且通過用戶聲明鑰匙鏈解密數(shù)據(jù)。加密數(shù)據(jù)的links同樣也被保護, 沒有解密鑰匙便不能遍歷。父對象和子對象使用不同的加密密鑰是可行。這種方式保證分享對象links的安全。

3.6 文件

IPFS同樣定義了一系列的對象來為Merkle DAG頂層的版本文件系統(tǒng)建立模型。對象模型類似于Git:

  1. block: 可變的數(shù)據(jù)塊
  2. list: 一個blocks或者別的list的集合
  3. tree: 一個blocks或者lists或者別的tree的集合
  4. commit: 版本歷史中一個tree的快照

我希望完全使用Git對象格式,但就需要分開來引入分布式系統(tǒng)中有用的特性。他們是, (a) 快速大小查詢(總子節(jié)已經(jīng)被添加到對象中), (b) 大文件去重(添加一個list對象), (c)將嵌入commits到trees中??傊?,IPFS文件對象很接近Git,兩者有可能進行交流。而且,一些Git對象可以在沒有丟失信息(比如UNIX文件權(quán)限等)的情況下轉(zhuǎn)換被引入。
注意: 文件對象使用JSON格式。雖然ipfs包括了JSON的導(dǎo)入導(dǎo)出,實際上這些結(jié)構(gòu)是使用protobufs的二進制編碼。

3.6.1文件對象: blob

blob對象包含了一個可尋址的數(shù)據(jù)單元, 并且標識一個文件。IPFS Blocks類似Git的blobs或者文件系統(tǒng)的數(shù)據(jù)塊。他們存儲著用戶的數(shù)據(jù)。注意, IPFS文件可以使用lists或者blobs來表示。blobs沒有l(wèi)inks。

{
"data": "some data here",
// blobs have no links
}

3.6.2文件對象: list

list對象是一個通過一些IPFS blobs聚合一起形成的大的或者去重的文件的表示。lists包含一個關(guān)于blob或者list的順序序列。IPFS list的作用類似文件系統(tǒng)中的間接blocks。由于lists可以包含其他的lists, 拓撲包含帶鏈接的lists和平衡數(shù)是有可能的。有向圖中相同的節(jié)點在不同的位置使得文件內(nèi)部去重。由于強制哈希尋址,環(huán)是不可能的。

{
"data": ["blob", "list", "blob"],
    // lists have an array of object types as data
"links": [
    { "hash": "XLYkgq61DYaQ8NhkcqyU7rLcnSa7dSHQ16x","size": 189458 },
    { "hash": "XLHBNmRQ5sJJrdMPuu48pzeyTtRo39tNDR5", "size": 19441 },
    { "hash": "XLWVQDqxo9Km9zLyquoC9gAP8CL1gWnHZ7z", "size": 5286 }
    // lists have no names in links
    ]
}

3.6.3文件對象: tree

IPFS中的tree對象類似Git中的。表示一個目錄, 一個名稱和哈希的映射。這些哈希表示著blobs, lists, 別的tree或者commits。注意傳統(tǒng)的路徑名稱已經(jīng)被Merkle DAG實現(xiàn)。

{
"data": ["blob", "list", "blob"],
    // trees have an array of object types as data
"links": [
  { "hash": "XLYkgq61DYaQ8NhkcqyU7rLcnSa7dSHQ16x",
    "name": "less", "size": 189458 },
  { "hash": "XLHBNmRQ5sJJrdMPuu48pzeyTtRo39tNDR5",
    "name": "script", "size": 19441 },
  { "hash": "XLWVQDqxo9Km9zLyquoC9gAP8CL1gWnHZ7z",
    "name": "template", "size": 5286 }
    // trees do have names
  ]
}

3.6.4文件對象: commit

commit對象表示著一個對象在版本歷史中的一個快照。和Git中的類似,不過可以引用任何類型??梢枣溄幼髡叩膶ο?。

{
"data": {
    "type": "tree",
    "date": "2014-09-20 12:44:06Z",
    "message": "This is a commit message."
},
"links": [
    { "hash": "XLa1qMBKiSEEDhojb9FFZ4tEvLf7FEQdhdU",
      "name": "parent", "size": 25309 },
    { "hash": "XLGw74KAy9junbh28x7ccWov9inu1Vo7pnX",
      "name": "object", "size": 5198 },
    { "hash": "XLF2ipQ4jD3UdeX5xp1KBgeHRhemUtaA8Vm",
      "name": "author", "size": 109 }
  ]
}

3.6.5版本控制

3.6.6文件系統(tǒng)路徑

通過Merkle DAG小節(jié), IPFS對象通過字符串路徑API遍歷。IPFS文件對象被設(shè)計成更容易掛載到UNIX文件系統(tǒng)上。為了表示trees為目錄,他們限制trees沒有數(shù)據(jù)。并且commit可以被表示成目錄或者完全隱藏在文件系統(tǒng)。

3.6.7文件分割成Lists和Blob

對于大文件的版本和分布式化的一個主要挑戰(zhàn)就是找到合理的方式來切分它成blocks。與其假設(shè)不同類型的文件有正確的切分法,不如說IPFS提供了如下選擇:

  • (a) 使用LBFS[?]中的Rabin Fingerprints[?]方法來選擇合適的block邊界。
  • (b) 使用rsync[?] rolling-checksum算法, 來檢測版本之間的塊變動。
  • (c) 允許用戶為制定的文件來設(shè)定塊分割算法

3.6.8路徑查詢性能

基于路徑訪問需要遍歷對象圖。檢索每一個對象需要查詢DHT中對應(yīng)的密鑰, 連接到對應(yīng)的對等節(jié)點, 檢索出它的blocks。這是很大的開銷,尤其是在查詢路徑中存在許多子路徑構(gòu)成。通過以下方式來緩和:

  • tree caching: 由于所有的對象都是哈希可尋址的, 他們可以被無限的緩存。此外, trees的大小更小一些,IPFS更傾向在blobs之上緩存它們。
  • flattened trees: 對于給定的tree, 一個特殊的flattened tree可以被構(gòu)建成一個鏈表, 所有對象從這個tree中訪問得到。flattened tree中的名稱可以分別通過原始tree以分隔符連接獲得。

例如, ttt111的flattened tree如下:

{
"data":
    ["tree", "blob", "tree", "list", "blob" "blob"],
"links": [
    { "hash": "<ttt222-hash>", "size": 1234
    "name": "ttt222-name" },
    { "hash": "<bbb111-hash>", "size": 123,
    "name": "ttt222-name/bbb111-name" },
    { "hash": "<ttt333-hash>", "size": 3456,
    "name": "ttt333-name" },
    { "hash": "<lll111-hash>", "size": 587,
    "name": "ttt333-name/lll111-name"},
    { "hash": "<bbb222-hash>", "size": 22,
    "name": "ttt333-name/lll111-name/bbb222-name" },
    { "hash": "<bbb222-hash>", "size": 22
    "name": "bbb222-name" }
] }

3.7 IPNS: 命名空間和可變狀態(tài)

目前為止, IPFS協(xié)議棧通過構(gòu)建一個內(nèi)容可尋址的DAG對象集形成了一個點對點塊交換系統(tǒng)。它用來發(fā)布和檢索不可變對象。它可以追蹤這些對象的版本變化。然而, 一個重要的部分缺失了: 可變的命名。沒有此部分, 所有以IPFS links通信的新內(nèi)容都會有所偏差。我們需要在一個相同的路徑下檢索可變的狀態(tài)。
如果可變的數(shù)據(jù)到最后是必須的, 我們必須明確為什么我們努力構(gòu)建了一套不可變的Merkle DAG。考慮到IPFS的特性: 對象可以(a)通過哈希檢索 (b)完整性檢查 (c)關(guān)聯(lián)到其他對象 (d)無限的緩存。某種意義上:
對象是永久的
這個也是高性能分布式系統(tǒng)的重要特性, 數(shù)據(jù)通過網(wǎng)絡(luò)links來移動是昂貴的。對象內(nèi)容可尋址構(gòu)建了一個具有以下特點的web:

  • (a) 顯著的帶寬優(yōu)化
  • (b) 非信任的內(nèi)容服務(wù)
  • (c) 永久的鏈接
  • (d) 任何一個對象和它的引用的完整永久性備份

基于不可變的內(nèi)容可尋址的對象和命名的Merkle DAG和指向Merkle DAG的可變的指針, 實例化了一個呈現(xiàn)在許多成功的分布式系統(tǒng)中的二分法。他們包括Git版本控制系統(tǒng), 擁有不可變的對象和可變的引用。Plan9[?]擁有可變的Fossil[?]和不可變的Venti[?]文件系統(tǒng)。LBFS[?]同樣擁有可變的indices和不可變的chunks。

3.7.1自我認證命名

通過SFS[12, 11]的命名方案,我們得到一個方式,在整個加密制定的命名空間中構(gòu)建自我認證的命名,并且是可變的。IPFS的方案如下:

  1. 回想IPFS中: NodeId = hash(node.PubKey)
  2. 我們給每一個用戶分配了一個可變的命名空間: /ipns/<NodeId>
  3. 一個用戶可以通過附上它的私鑰簽名發(fā)布一個對象到這個路徑上, 比如:/ipns/XLF2ipQ4jD3UdeX5xp1KBgeHRhemUtaA8Vm/
  4. 當別的用戶檢索這個對象時, 他們可以檢查這個簽名是否匹配公鑰和NodeId。這將驗證用戶發(fā)布對象的真實性,獲取可變狀態(tài)的查詢。

注意細節(jié):

  • ipns(InterPlanetary Name Space)獨立的前綴是為了建立一個簡單的識別區(qū)分來區(qū)分可變和不可變的路徑, 為了程序也為了人們閱讀。

  • 由于這不是一個內(nèi)容可尋址的對象,發(fā)布它需要依賴IPFS可變狀態(tài)分發(fā)系統(tǒng), 路由系統(tǒng)。過程是(1)按照常規(guī)不可變IPFS對象發(fā)布對象, (2) 在路由系統(tǒng)中發(fā)布它的哈希作為一個meta元數(shù)據(jù)值:

    routing.setValue(NodeId, <ns-object-hash>)
    
  • 被發(fā)布的對象的任何links被視為命名空間的子名稱:

    /ipns/XLF2ipQ4jD3UdeX5xp1KBgeHRhemUtaA8Vm/
    /ipns/XLF2ipQ4jD3UdeX5xp1KBgeHRhemUtaA8Vm/docs
    /ipns/XLF2ipQ4jD3UdeX5xp1KBgeHRhemUtaA8Vm/docs/ipfs
    
  • 被推薦發(fā)布一個commit對象, 或者版本歷史系統(tǒng)的別的對象, 以至于客戶端可以找到舊的名稱。這樣便留給用戶一個選擇。

注意當用戶發(fā)布這個對象時, 它不能用相同的方式發(fā)布。

3.7.2人類可讀命名

IPNS確實是一種方式來分發(fā)和再分發(fā)命名, 不過它還是暴露的長長的hash值作為命名, 對于用戶不是很友好,很難記憶。IPFS通過一下技術(shù)增強用戶友好的IPNS。

Peer Links

收到SFS的啟發(fā), 用戶可以直接關(guān)聯(lián)別人的對象到自己的對象中(命名空間,home)。這樣同樣增加了網(wǎng)絡(luò)信任。

# Alice links to bob Bob
ipfs link /<alice-pk-hash>/friends/bob /<bob-pk-hash>
# Eve links to Alice
ipfs link /<eve-pk-hash/friends/alice /<alice-pk-hash>
# Eve also has access to Bob
/<eve-pk-hash/friends/alice/friends/bob
# access Verisign certified domains
/<verisign-pk-hash>/foo.com
DNS TXT IPNS 記錄

如果/ipns/<domain>是一個有效的域名名稱, IPFS可以在自己的DNS TXT記錄中查找ipns鍵。IPFS翻譯那個值或者作為一個對象hash或者另一個IPNS路徑:

# this DNS TXT record
ipfs.benet.ai. TXT "ipfs=XLF2ipQ4jD3U ..."
# behaves as symlink
ln -s /ipns/XLF2ipQ4jD3U /ipns/fs.benet.ai
Proquint Pronounceable Identifiers

有方案將二進制轉(zhuǎn)換成可發(fā)音的單詞。IPNS支持Proquint[?]。如下:

# this proquint phrase
/ipns/dahih-dolij-sozuk-vosah-luvar-fuluh
# will resolve to corresponding
/ipns/KhAwNprxYVxKqpDZ
Name Shortening Services

涌現(xiàn)出提供縮短名稱的服務(wù), 向用戶提供他們的命名空間。就像我們現(xiàn)在看到的DNS和Web的URLs:

# User can get a link from
/ipns/shorten.er/foobar
# To her own namespace
/ipns/XLF2ipQ4jD3UdeX5xp1KBgeHRhemUtaA8Vm

3.8 IPFS使用場景

IPFS設(shè)計為可以使用多種不同的方法來使用的,下面就是一些我將會繼續(xù)追求的使用方式:

  1. 作為一個掛載的全局文件系統(tǒng),掛載在/ipfs和/ipns下
  2. 作為一個掛載的個人同步文件夾,自動的進行版本管理,發(fā)布,以及備份任何的寫入
  3. 作為一個加密的文件或者數(shù)據(jù)共享系統(tǒng)
  4. 作為所有軟件的版本包管理者
  5. 作為虛擬機器的根文件系統(tǒng)
  6. 作為VM的啟動文件系統(tǒng) (在管理程序下)
  7. 作為一個數(shù)據(jù)庫:應(yīng)用可以直接將數(shù)據(jù)寫入Merkle DAG數(shù)據(jù)模型中,獲取所有的版本,緩沖,以及IPFS提供的分配
  8. 作為一個linked(和加密的)通信平臺
  9. 作為一個為大文件的完整性檢查CDN(不使用SSL的情況下)
  10. 作為一個加密的CDN
  11. 在網(wǎng)頁上,作為一個web CDN
  12. 作為一個links永遠存在新的永恒的Web

IPFS實現(xiàn)的目標:

  • (a)一個IPFS庫可以導(dǎo)出到你自己應(yīng)用中使用
  • (b)命令行工具可以直接操作對象
  • (c)使用FUSE[?]或者內(nèi)核的模型掛載文件系統(tǒng)

4. 未來

5. 感謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容