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