從數據的角度帶你深刻了解IPFS

  

IPFS 和區塊鏈有着很是緊密的聯繫, 隨着區塊鏈的不斷髮展,對數據的存儲需求也愈來愈高。本文從IPFS 的底層設計出發, 結合源代碼, 分析了IPFS 的一些技術細節。javascript


1、概述

IPFS 和區塊鏈有着很是緊密的聯繫, 隨着區塊鏈的不斷髮展,對數據的存儲需求也愈來愈高, 因爲性能和成本的限制,現有的區塊鏈設計方案大部分都選擇了把較大的數據存儲在鏈外,經過對數據進行加密, 哈希運算等手段來防止數據被篡改, 在區塊鏈上只引用所存數據的hash 值, 從而知足業務對數據的存儲需求。 本文從IPFS 的底層設計出發, 結合源代碼, 分析了IPFS 的一些技術細節。 因爲IPFS還在不斷更新中, 文中引用的部分可能和最新代碼有所出入。java

閱讀本文須要讀者node

  • 瞭解網絡編程
  • 瞭解分佈式存儲
  • 瞭解基本的區塊鏈知識

 

2、什麼是IPFS?

維基百科上是這樣解釋的:是一個旨在建立持久且分佈式存儲和共享文件的網絡傳輸協議。git

上面的解釋稍顯晦澀, 個人理解是:算法

1. 首先它是一個FS(文件系統)編程

2. 其次它支持點對點傳輸json

既然是文件系統, 那它和普通的文件系統有什麼區別呢? 有如下幾點區別:bootstrap

  • 存儲方式: 它是分佈式存儲的, 爲了方便傳輸,文件被切分紅多個block, 每一個block 經過hash運算獲得惟一的ID, 方便在網絡中進行識別和去重。 考慮到傳輸效率, 同一個block 可能有多個copy, 分別存儲在不一樣的網絡節點上。
  • 內容尋址方式: 每一個block都有惟一的ID,咱們只須要根據節點的ID 就能夠獲取到它所對應的block。

那麼問題來了, 既然文件被切分紅了多個block,如何組織這些block 數據,組成邏輯上的文件呢? 在IFPS中採用的merkledag, 下面是 merkledag的一個示意圖:瀏覽器

簡單來講, 就是2種數據結構merkle 和DAG(有向無環圖)的結合, 經過這種邏輯結構, 能夠知足:安全

  • 內容尋址: 使用hash ID來惟一識別一個數據塊的內容
  • 防篡改: 能夠方便的檢查哈希值來確認數據是否被篡改
  • 去重: 因爲內容相同的數據塊哈希是相同的,能夠很容去掉重複的數據,節省存儲空間

肯定了數據模型後, 接下來要作的事: 如何把數據分發到不一樣的網絡節點上, 達到分佈式存儲和共享的目的? 咱們先思考一下, 經過網絡,好比HTTP, 訪問某個文件的步驟,首先咱們要知道存儲這個文件的服務器地址, 而後咱們須要知道這個文件對應的ID, 好比文件名。前者咱們能夠抽象成網絡節點尋址, 後者咱們抽象成文件對象尋址; 在IPFS中, 這兩種尋址方式使用了相同的算法, KAD, 介紹KAD算法的文章不少,這裏不贅述, 只簡單說明一下核心思想:

KAD 最精妙之處就是使用XOR 來計算ID 之間的距離,而且統一了節點ID 和 對象ID的尋址方式。 採用 XOR(按比特異或操做)算法計算 key 之間的「距離」。

這種作法使得它具有了相似於「幾何距離」的某些特性(下面用 ⊕ 表示 XOR)

  • (A ⊕ B) == (B ⊕ A) XOR 符合「交換律」,具有對稱性。
  • (A ⊕ A) == 0 反身性,自身距離爲零
  • (A ⊕ B) > 0 【不一樣】的兩個 key 之間的距離必大於零
  • (A ⊕ B) + (B ⊕ C) >= (A ⊕ C) 三角不等式

經過KAD算法,IPFS 把不一樣ID的數據塊分發到與之距離較近的網絡節點中,達到分佈式存儲的目的。

經過IPFS獲取文件時,只須要根據merkledag, 按圖索驥,根據每一個block的ID, 經過KAD算法從相應網絡節點中下載block數據, 最後驗證是否數據完整, 完成拼接便可。

下面咱們再從技術實現的角度作更深刻的介紹。

3、IPFS的系統架構

咱們先看一下IPFS的系統架構圖, 分爲5層:

  • 一層爲naming, 基於PKI的一個命名空間;
  • 第二層爲merkledag, IPFS 內部的邏輯數據結構;
  • 第三層爲exchange, 節點之間block data的交換協議;
  • 第四層爲routing, 主要實現節點尋址和對象尋址;
  • 第五層爲network, 封裝了P2P通信的鏈接和傳輸部分。

站在數據的角度來看, 又能夠分爲2個大的模塊:

  • IPLD( InterPlanetary Linked Data) 主要用來定義數據, 給數據建模;
  • libp2p解決的是數據如何傳輸的問題。

下面分別介紹IFPS 中的2個主要部分IPLD 和 libP2P。

 

1.IPLD

經過hash 值來實現內容尋址的方式在分佈式計算領域獲得了普遍的應用, 好比區塊鏈, 再好比git repo。 雖然使用hash 鏈接數據的方式有類似之處, 可是底層數據結構並不能通用, IPFS 是個極具野心的項目, 爲了讓這些不一樣領域之間的數據可互操做, 它定義了統一的數據模型IPLD, 經過它, 能夠方便地訪問來自不一樣領域的數據。

前面已經介紹數據的邏輯結構是用merkledag表示的, 那麼它是如何實現的呢? 圍繞merkledag做爲核心, 它定義瞭如下幾個概念:

  • merkle link 表明dag 中的邊
  • merkel-dag 有向無環圖
  • merkle-path 訪問dag節點的相似unix path的路徑
  • IPLD data model 基於json 的數據模型
  • IPLD serialized format 序列化格式
  • canonical 格式: 爲了保證一樣的logic object 老是序列化爲一個一樣的輸出, 而制定的肯定性規則

圍繞這些定義它實現了下面幾個components

  • CID 內容ID
  • data model 數據模型
  • serialization format 序列化格式
  • tools & libraries 工具和庫
  • IPLD selector 相似CSS 選擇器, 方便選取dag中的節點
  • IPLD transformation 對dag 進行轉換計算

咱們知道,數據是多樣性的,爲了給不一樣的數據建模, 咱們須要一種通用的數據格式, 經過它能夠最大程度地兼容不一樣的數據, IPFS 中定義了一個抽象的集合, multiformat, 包含multihash、multiaddr、multibase、multicodec、multistream幾個部分。

 

(一)multihash

自識別hash, 由3個部分組成,分別是:hash函數編碼、hash值的長度和hash內容, 下面是個簡單的例子:

這種設計的最大好處是很是方便升級,一旦有一天咱們使用的hash 函數再也不安全了, 或者發現了更好的hash 函數,咱們能夠很方便的升級系統。

 

(二)multiaddr

自描述地址格式,能夠描述各類不一樣的地址

 

(三)multibase

multibase 表明的是一種編碼格式, 方便把CID 編碼成不一樣的格式, 好比這裏定義了2進制、8進制、10進制、16進制、也有咱們熟悉的base58btc 和 base64編碼。

 

(四)multicodec

mulcodec 表明的是自描述的編解碼, 實際上是個table, 用1到2個字節定了數據內容的格式, 好比用字母z表示base58btc編碼, 0x50表示protobuf 等等。

 

五)multistream

multistream 首先是個stream, 它利用multicodec,實現了自描述的功能, 下面是基於一個javascript 的例子; 先new 一個buffer 對象, 裏面是json對象, 而後給它加一個前綴protobuf, 這樣這個multistream 就構造好了, 能夠經過網絡傳輸。在解析時能夠先取codec 前綴,而後移除前綴, 獲得具體的數據內容。

結合上面的部分, 咱們重點介紹一下CID。
CID 是IPFS分佈式文件系統中標準的文件尋址格式,它集合了內容尋址、加密散列算法和自我描述的格式, 是IPLD 內部核心的識別符。目前有2個版本,CIDv0 和CIDv1。

CIDv0是一個向後兼容的版本,其中:

  • multibase 一直爲 base58btc
  • multicodec 一直爲 protobuf-mdag
  • version 一直爲 CIDv0
  • multihash 表示爲cidv0 ::= <multihash-content-address>

爲了更靈活的表述ID數據, 支持更多的格式, IPLD 定義了CIDv1,CIDv1由4個部分組成:

  • multibase
  • version
  • multicodec
  • multihash

IPLD 是IPFS 的數據描述格式, 解決了如何定義數據的問題, 下面這張圖是結合源代碼整理的一份邏輯圖,咱們能夠看到上面是一些高級的接口, 好比file, mfs, fuse 等。 下面是數據結構的持久化部分,節點之間交換的內容是以block 爲基礎的, 最下面就是物理存儲了。好比block 存儲在blocks 目錄, 其餘節點之間的信息存儲在leveldb, 還有keystore, config 等。

 

2.數據如何傳輸呢?

接下來咱們介紹libP2P, 看看數據是如何傳輸的。libP2P 是個模塊化的網絡協議棧。

作過socket編程的小夥伴應該都知道, 使用raw socket 編程傳輸數據的過程,無非就是如下幾個步驟:

  1. 獲取目標服務器地址
  2. 和目標服務器創建鏈接
  3. 握手協議
  4. 傳輸數據
  5. 關閉鏈接

libP2P 也是這樣,不過區別在於它把各個部分都模塊化了, 定義了通用的接口, 能夠很方便的進行擴展。

(一)架構圖

由如下幾個部分組成,分別是:

  • Peer Routing
  • Swarm (傳輸和鏈接)
  • Distributed Record Store
  • Discovery

下面咱們對它們作分別介紹, 咱們先看關鍵的路由部分。

 

(二)Peer Routing

libP2P定義了routing 接口,目前有2個實現,分別是KAD routing 和 MDNS routing, 擴展很容易, 只要按照接口實現相應的方法便可。

ipfs 中的節點路由表是經過維護多個K-BUCKET來實現的, 每次新增節點, 會計算節點ID 和自身節點ID 之間的common prefix, 根據這個公共前綴把節點加到對應的KBUCKET 中, KBUCKET 最大值爲20, 當超出時,再進行拆分。

更新路由表的流程以下:

除了KAD routing 以外, IPFS 也實現了MDNS routing, 主要用來在局域網內發現節點, 這個功能相對比較獨立, 因爲用到了多播地址, 在一些公有云部署環境中可能沒法工做。

 

(三)Swarm(傳輸和鏈接)

swarm 定義瞭如下接口:

  • transport 網絡傳輸層的接口
  • connection 處理網絡鏈接的接口
  • stream multiplex 同一connection 複用多個stream的接口

下面咱們重點看下是如何動態協商stream protocol 的,整個流程以下:

  1. 默認先經過multistream-select 完成握手
  2. 發起方嘗試使用某個協議, 接收方若是不接受, 再嘗試其餘協議, 直到找到雙方都支持的協議或者協商失敗。

另外爲了提升協商效率, 也提供了一個ls 消息, 用來查詢目標節點支持的所有協議。

 

(四)Distributed Record Store

record 表示一個記錄, 能夠用來存儲一個鍵值對,好比ipns name publish 就是發佈一個objectId 綁定指定 node id 的record 到ipfs 網絡中, 這樣經過ipns 尋址時就會查找對應的record, 再解析到objectId, 實現尋址的功能。

 

(五)Discovery

目前系統支持3種發現方式, 分別是:

  • bootstrap 經過配置的啓動節點發現其餘的節點
  • random walk 經過查詢隨機生成的peerID, 從而發現新的節點
  • mdns 經過multicast 發現局域網內的節點

最後總結一下源代碼中的邏輯模塊:

從下到上分爲5個層次:

  • 最底層爲傳輸層, 主要封裝各類協議, 好比TCP,SCTP, BLE, TOR 等網絡協議
  • 傳輸層上面封裝了鏈接層,實現鏈接管理和通知等功能
  • 鏈接層上面是stream 層, 實現了stream的多路複用
  • stream層上面是路由層
  • 最上層是discovery, messaging以及record store 等

4、總結

本文從定義數據和傳輸數據的角度分別介紹了IPFS的2個主要模塊IPLD 和 libP2P:

  • IPLD 主要用來定義數據, 給數據建模
  • libP2P 解決數據傳輸問題

這兩部分相輔相成, 雖然都源自於IPFS項目,可是也能夠獨立使用在其餘項目中。

IPFS的遠景目標就是替換如今瀏覽器使用的 HTTP 協議, 目前項目還在迭代開發中, 一些功能也在不斷完善。爲了解決數據的持久化問題, 引入了filecoin 激勵機制, 經過token激勵,讓更多的節點加入到網絡中來,從而提供更穩定的服務。

相關文章
相關標籤/搜索