1. 程式人生 > 其它 >資料格式_IPFS IPLD 資料格式擴充套件實踐

資料格式_IPFS IPLD 資料格式擴充套件實踐

技術標籤:資料格式

1. 概述

IPLD(Inter Planetary Linked Data)在IPFS起著非常重要的作用,其概念性的介紹可以參考Protocol Labs的專案官方網站。本文主要介紹如何整合一個類似新區塊鏈專案的資料塊到IPFS系統中,從而實現區塊資料在IPFS系統的儲存、解析等功能。

目前IPFS有go和Javascript等多種語言的實現方式,本文主要從go語言的程式碼系統來介紹。在go語言的IPFS系統中除了內部已經實現了JSON、RAW、CBOR以及Protobuf格式的資料儲存外,同時也提供一個Plugin的方式來支援、實現新的IPLD 資料格式的擴充套件與支援,新的資料格式通過一個Plugin來實現IPLD所要求的介面即可完成整合。由於這個Plugin是基於go語言的外掛(把go語言實現程式碼轉換成動態庫檔案,比如linux的so檔案)機制來實現的,其存在windows系統中不支援的限制。

2. 實踐

2.1 IPFS plugin載入介紹

在IPFS daemon啟動過程中會呼叫一個makeExecutor的函式,該函式的一個主要工作就是載入和初始化IPFS的plugins,IPFS的外掛預設是存放home目錄(以linux為例)的.ipfs/plugins中。程式碼參考如下:

826f58ec9ef89007b98ddb378dd89458.png

在LoadPlugins這個函式中完成了plugin的載入和初始化工作:

// Plugin is base interface for all kinds of go-ipfs plugins// It will be included in interfaces of different Pluginstype Plugin interface {   // Name should return unique name of the plugin   Name() string   // Version returns current version of the plugin   Version() string   // Init is called once when the Plugin is being loaded   Init() error}// PluginIPLD is an interface that can be implemented to add handlers for// for different IPLD formatstype PluginIPLD interface {   Plugin   RegisterBlockDecoders(dec ipld.BlockDecoder) error   RegisterInputEncParsers(iec coredag.InputEncParsers) error}

這裡重點要關注的是:

1) LoadDynamicPlugins需要每個so檔案有一個"Plugins"的全域性變數符號,通過這個變數來告訴IPFS這個so檔案有幾個plugins從而進一步獲取到每個plugin的例項。比如GIT plugin的程式碼:

// Plugins is exported list of plugins that will be loadedvar Plugins = []plugin.Plugin{   &gitPlugin{},}

2) 初始化initialize和run的實現: 通過"Plugins"獲取到plugin的具體例項後initialize會呼叫每個plugin的Init的函式,通過這個介面我們可以判斷所實現plugin是否成功載入;run會把plugin所實現資料格式的編解碼函式註冊到BlockDecoder中(IPFS資料是以block形式儲存),完成這一步後一個IPLD新資料格式就基本具備資料匯入和資料解析的功能了,同時plugin的基本輪廓也形成了。

2.2 IPFS plugin實現

要實現一個IPFS plugin基本上就是要實現:一個變數,這個變數指的就是前面介紹過的"Plugins";兩個介面,即PluginIPLD 和Node兩套介面。下面分別對兩個介面做介紹:

1) IPFS plugin介面

// Plugin is base interface for all kinds of go-ipfs plugins// It will be included in interfaces of different Pluginstype Plugin interface {   // Name should return unique name of the plugin   Name() string   // Version returns current version of the plugin   Version() string   // Init is called once when the Plugin is being loaded   Init() error}// PluginIPLD is an interface that can be implemented to add handlers for// for different IPLD formatstype PluginIPLD interface {   Plugin   RegisterBlockDecoders(dec ipld.BlockDecoder) error   RegisterInputEncParsers(iec coredag.InputEncParsers) error}

前面載入流程介紹中已經提到呼叫plugin的相關介面函式,其中RegisterInputEncParsers 、RegisterBlockDecoders分別向IPFS的BlockDecoder註冊編、解碼函式,RegisterInputEncParsers註冊的函式負責把外部資料按照IPFS DAG Node的格式進行編碼,反之RegisterBlockDecoders註冊的函式負責解析編碼的Node資料,這樣IPFS在查詢到一個該資料格式的Block後就能正確解析對應的資料。實現範例:

func (*gitPlugin) RegisterBlockDecoders(dec format.BlockDecoder) error { dec.Register(cid.GitRaw, git.DecodeBlock) return nil}

GitRaw是 GIT資料格式所對應的codec型別,這個後面會介紹。

func (*gitPlugin) RegisterInputEncParsers(iec coredag.InputEncParsers) error { iec.AddParser("raw", "git", parseRawGit) iec.AddParser("zlib", "git", parseZlibGit) return nil}

IPFS 儲存物件介面

IPFS 資料是以M-DAG(Merkle有向無環圖)作為資料結構來描述資料物件的關係,圖中的每一個節點是一個物件(實際資料),兩個節點之間的邊是一個Link。IPFS DAG中的每一個節點就是一個Node。既然資料是一個Node形式來表現,為此對每一個Plugin來說自然就需要實現Node介面所對應的函式(或方法),從而保證整個DAG圖的完整、正確的解析。

DAG中的每一個Node都有一個Cid,在IPFS系統中看到的資料HASH正是來之這個Cid資料結構。Cid 是IPFS分散式檔案系統中標準的檔案定址格式,它集合了內容定址、加密雜湊演算法和自我描述的格式, 是IPLD 內部核心的識別符。目前有2個版本: CIDv0 和CIDv1,在實際資料格式擴充套件應用中基本上是v1版本(v0是ProtoBuf格式)。

type Cid struct { version uint64 codec uint64 hash mh.Multihash}type BlockDecoder interface { Register(codec uint64, decoder DecodeBlockFunc) Decode(blocks.Block) (Node, error)}

對一個新的IPLD資料格式就需要定義一個新的codec,然後BlockDecoder根據這個codec來註冊編解碼函式(前面已介紹)。

// Block provides abstraction for blocks implementations.type Block interface { RawData() []byte Cid() *cid.Cid String() string Loggable() map[string]interface{}}// Node is the base interface all IPLD nodes must implement. Nodes are **Immutable** and all methods defined on the interface are// **Thread Safe**.type Node interface { blocks.Block Resolver // ResolveLink is a helper function that calls resolve and asserts the // output is a link ResolveLink(path []string) (*Link, []string, error) // Copy returns a deep copy of this node Copy() Node // Links is a helper function that returns all links within this object Links() []*Link // TODO: not sure if stat deserves to stay Stat() (*NodeStat, error) // Size returns the size in bytes of the serialized object Size() (uint64, error)}

在完成上述的PluginIPLD以及Node介面函式的實現後一個Plugin就基本完成了,然後通過一個全域性的“Plugins“變數來暴露出該plugin so檔案所支援的plugin。Plugin實現後接下來就是整合到IPFS系統中,Plugin的整合有兩種方式:一是直接把相關程式碼放到IPFS的Plugins目錄中進行統一編譯(可參考IPFS已實現的GIT plugin),這種方式不方便後續維護,每次更新IPFS程式碼都需要重新整合;二是直接把plugin的程式碼單獨編譯,然後把編譯後的plugin so檔案放到IPFS 的plugins輸出目錄即可,這種方式要注意的是需要按照Go語言的Plugin編譯方式來編譯,類似:go build -buildmode=plugin -o=***.so 。

2.3 Plugin驗證

命令列的方式:

cat file | ipfs dag put --input-enc raw –format new_format

file是需要匯入的檔名,new_format是plugin實現的新資料格式,比如GIT plugin介紹中的git.

在提交這個命令後就會呼叫對應的plugin所註冊的encode函式,比如GIT plugin中的parseRawGit。

程式碼方式:

直接呼叫coredag.ParseInputs,比如coredag. ParseInputs (“raw”, “git”, file, mhType, -1)。實際行命令列執行時呼叫的就是這個函式完成相關動作。