以下是李冠男的分享,由巴比特整理。
大家都知道,區塊鏈的故事開始于2008年中本聰發表《一種點對點的電子現金系統》,之后比特幣橫空出世。后來又出現了以太坊,隨著市場熱度的高漲,人們發現了區塊鏈技術本身的應用價值,各種項目層出不窮,但是這其中的很多場景需要區塊鏈具備文件存儲能力。
一般來講,大家的第一反應是,我能不能把數據都存儲在鏈上?但是現有的主流區塊鏈,比特幣就不用說了,以太坊上面存數據是非常昂貴的,按照Gas數據是5Gwei計算,存儲1MB數據需要花費3.76ETH。Hyperledger Fabric因為是聯盟鏈,把數據硬要存在上面也是可以的,但現在有個寫死的限制,默認數據小于99M,如果大于的話,需要重新編譯它的代碼。
所以可以看到,數據全部上鏈并不明智,也沒有必要像存儲交易數據一樣,讓千百兆的數據文件存儲在每一個節點上。所以通常的做法是: 將文件存儲在鏈外,在鏈上存儲文件的hash ,這樣文件其實依然是中心化存儲,比如傳統的“云存儲“。
當前多采用的中心化“云存儲”
什么是“云存儲”?
傳統云存儲是讓用戶上傳自己的數據到云端,用戶上傳完畢后,由服務提供商將數據保存在他們的數據中心。這樣用戶無論何時何地想要訪問這些信息的時候,只需要向數據中心發送一條請求,數據中心將數據發給用戶。
中心化的“云存儲”存在以下問題:
典型的問題是數據中心都是大型服務器,它需要溫控,并且嚴格維護,成本高昂,而且會有延遲,因為通常數據中心與用戶不會距離很近。有人說,可以使用CDN,但問題是它的隱私策略是由服務提供商設計的,他們依然有辦法訪問和分享用戶的個人數據,畢竟是不透明的。而且除了作惡的可能,只要有人工牽扯進去,就很可能會有意外的錯誤。比如員工誤刪數據庫的事件并不罕見,GitLab事件讓人記憶猶新…
采用“分布式存儲網絡”優點眾多
所以我們需要采用分布式存儲網絡。這個技術并不新鮮,它有很多優點,存儲的文件大小不受限制,可以無限擴展存儲容量,而且成本低,不受地域限制,還是去中心化的,如果應用特點的技術,也可以保證它的內容不被篡改。
如果將“分布式存儲網絡”和“區塊鏈”技術結合起來,是不是就可以解決區塊鏈天然不易存儲大文件的這種問題?
我選用的就是IPFS技術,中文叫做星際文件系統。
分布式存儲IPFS——What?How?
IPFS是什么?
? IPFS是分布式存儲網絡; ? IPFS是一個點對點的超媒體協議,目的是讓現有的網絡更快、更安全、更開放; ? IPFS有一個很瘋狂的目標:致力于替代HTTP協議,為所有人建造一 個更好的網絡 ;
那么它有什么特點呢?
首先,存儲在IPFS上的文件,是被打散存儲的。就是說每個文件以及所有的文件塊都有獨一無二的指紋,這個指紋就是一個加密哈希值。其次,IPFS網絡可以自動去除重復文件,也可以跟蹤每個文件的版本歷史 。而且每個網絡節點只需要存儲自己感興趣的內容以及一些索引信息,這些索引信息的作用就是用來找到誰存儲了什么 。當查找文件的時候,你可以使用Hash詢問IPFS網絡哪些節點存儲了什么內容。最后,每個文件可以通過去中心化的命名系統IPNS獲得對人友好的名字 而不是一串看花眼的hash。
IPFS技術棧分為這么幾層:
最底層當然是網絡、然后是路由、交換層、特定的結構層、Merkledag、命名系統,最上面是應用。IPFS和區塊鏈一樣,是技術集大成者,它借鑒了很多相關的技術。最下面三層的作用是轉移數據,往上的兩層是定義數據,最上面當然就是使用數據了。
在介紹完基礎工具后,正式講解我的嘗試。
基于Fabric的存儲擴展實踐
首先,我明確了三個目標:
1.我希望Fabric能夠暴露出 一組接口,使得外部可以通過Fabric使用IPFS;
2.我希望接下來Chaincode可以在有需要時也可以直接使用IPFS這個功能;
3.我希望這個過程中盡量少的修改現有Fabric代碼。換句話說,我希望這是一種系統的能力,而不是一種特定的應用層手段。
目標擺在這兒了,最直接的方式是以現有的go-sdk作為粘合劑,將Fabric和IPFS聯合起來。
最開始的想法是:對go-sdk進行二次開發,把與IPFS交互的邏輯封裝其中,實際上在使用過程中,由sdk先請求 IPFS得到返回,再將返回結果作為交易內容寫入Fabric ,和大部分現在的用法是一樣的。
另外IPFS有的幾個特點需要注意:一是它有自己的垃圾回收機制,當你寫入某個節點的數據,只有經過稱為“pin”的操作才能保證不被回收掉。二是不同節點之間不會自動備份同步數據,除非主動發起請求 。
基于這些想法,就誕生了第一個方案:
方案一
很簡單,通過sdk改造一下,用sdk去操作IPFS網絡。把這個文件添加pin之后得到哈希返回,然后把哈希值封裝到一般的交易里面,再提交給Fabric網絡。
這個方案有幾個明顯特點: 首先,IPFS與Fabric是相互獨立的,沒有直接的交互。IPFS作為外部系統由sdk調用 ,這樣做的優點是完全不用修改Fabric代碼和內部流程。但缺點是,今后辦法直接使用IPFS的。
我們來看看Fabric 的Chaincode 的是怎么工作的?大家都知道Chaincode是運行在docker容器中的,它和peer是通過GRPC通信的。
通常,Chaincode首先需要注冊,注冊之后初始化,然后才能運行存儲信息這樣的操作,再往后就是一些判活操作了。但是這種交互它其實有一個中間層,是通過Shim組件去交互的,Shim中的 chaincodeStubInterface 定義了在chaincode中可以使用的功能。 所以我就誕生了第二個想法,若是想 chaincode使用IPFS,對shim進行擴展來實現。
方案二
這個方案的想法就是先去擴展shim.ChaincodeStubInterface,添加實現相應的功能函數,光添加函數肯定不行,還需要在Fabric內開辟一個模塊,專門用來處理IPFS相關的請求,這樣就可以像調用GetState/PutState一樣調用類似GetFile/PutFile的函數來獲取/存儲文件了。
這個方案里,peer是Chaincode 的代理,可以與IPFS網絡直接去交互。整個想法其實很簡單,但真正實現過程中,我發現并不容易,因為需要修改Fabric的很多代碼,另外這個方案仍然是需要應用去主動維護文件的狀態。IPFS網絡節點是不會主動去備份這些文件的。跟第一個方案相比,它已經實現了讓Chaincode直接使用IPFS,雖然缺點很多,但看上去方向應該沒錯。
這個時候我剛好注意到,Fabric1.2中有一個不錯的特性,這也得益于它的架構設計,一切設計都是可插拔的。Fabric1.2中系統鏈碼會在每個新創建的channel自動部署一套 ,并且不同的channel之間實現了隔離。所以我們跳出來看,讓chaincode使用IPFS,即讓chaincode調用外部的途徑都有什么呢? 可不可以借助Fabric系統可插拔的特性去實現這個功能呢?我看到ChaincodeStubInterface中有一個功能函數叫InvokeChaincode,這個功能很好,它可以讓一個Chaincode去調用另一個Chaincode,這讓我想到了第三個方案。
方案三
我完全可以把與IPFS交互的邏輯包裝成一個系統代碼,然后應用可插拔特性把它集成進去。然后就可以用InvokeChaincode的功能,獲得相應的能力。而且好處還不少。首先它可以單獨維護,修改和升級都很方便。User Chaincode可以通過我剛說的這個功能,調用IPFScc來使用IPFS,也不用修改代碼,并且可以把文件索引存儲在IPFScc namespace中,這樣由系統實現文件狀態監控也可行了 。
而且我可以其實靈活一些,把方案一里的辦法集成進來,讓sdk去開發一組接口,讓它可以和IPFS網絡進行交互,得到的hash上傳到賬本里也是可以的。
還有最后一個問題就是剛才提到的,把文件上傳進去之后,如果你不去操作的話,它不會主動備份。這個問題怎么解決?最后我是通過IPFS-cluster,它有自己提供的共識機制,而且能自動備份IPFS網絡中的文件,還可以靈活配置備份數量。
這樣的話流程就比較清楚了。我可以通過Sdk/CLI上傳文件/由chaincode 根據需要在peer端臨時生成文件并上載IPFS網絡,得到返回hash,通過IPFS- cluster pin 文件,成功后將文件信息及相關IPFS 索引信息寫入IPFScc的ledger 。這樣的話其實應用的空間也就被打開了。