區塊鏈與你“最熟悉的陌生人”

提起“默克爾樹作為底層數據結構的分佈式系統”你會想起誰?除了區塊鏈,其實還有你最熟悉的陌生人——Git。


“簡化版”的區塊鏈


從許多角度看,Git都像簡化版的區塊鏈。

Git的開發始於2005年。彼時,Linux內核開發團隊正被之前使用的專有代碼管理系統BitKeeper所困擾,Linus Torvalds希望獲得一種體驗近似BitKeeper的分佈式系統,遍尋不得,便選擇了自行開發。


區塊鏈與你“最熟悉的陌生人”



Git項目開發效率驚人——Linus 4月3日開工,6日向社區宣佈,7日實現self-hosting,18日第一批分枝合併,29日就能以每秒6.7次的速度向Linux內核代碼樹打補丁。6月,在Git的控制下,便發佈了2.6.12版內核。

如果用三句話闡述Git的運行原理,那就是:

  1. 生成修改過的文件
  2. 生成當前目錄 tree 文件,關聯當前狀態文件
  3. 生成commit文件,關聯到當前目錄tree文件,並記下父 commit



區塊鏈與你“最熟悉的陌生人”



其使用方式可簡單描述為:本地提交,累積幾次後push到remote。本次提交會關聯上一次提交,跟區塊鏈是不是類似?版本控制最重要的是可追溯,如果某次錯誤提交,還可以回退到歷史版本——可追溯也是區塊鏈的重要特性。

區塊鏈是分佈式的,Git天然就是分佈式,不過Git依賴文件系統。以GitHub上的操作為例,代碼或者文檔一旦提交,操作將無法撤銷。如果程序員clone repo,只要不刪除,將永久存儲在自身電腦,除非文件系統崩潰;如果某程序員fork該repo,只要賬戶不被刪除,這個repo將永久保留在賬戶之下。

另外,某個repo fork、clone次數越多,被摧毀的概率也就越低;再者,某個repo即使最近一次操作清空了所有代碼,還可以通過git log恢復。

區塊鏈的另一個特性是不可篡改,也就是隻能Insert。Git呢?GitHub託管的repo裡的內容本身是可以修改的,然而這個commit歷史卻是無法修改的。每一次commit都有唯一標誌,本次commit會有parent commit的信息。Git產生的log也可以通區塊鏈數據庫類比。

而且,誰能說“不可修改”或者具備共識算法就是可稱為區塊鏈的充分條件呢?

區塊鏈與你“最熟悉的陌生人”


如果將視角轉向底層,我們能發現兩者更多相似。

共同的底層數據結構——默克爾樹


區塊鏈與Git內部數據結構都以樹形數據對象表示——即以默克爾樹(Merkle Tree)作為底層數據結構。

默克爾樹這種現代數據結構是由計算機科學家Ralph Merkle(他也是公鑰加密算法共同設計者)在1979年提出(作為對比,Knuth的TAOCP三卷第一版寫完是1973年),並以他的名字命名。


區塊鏈與你“最熟悉的陌生人”



這種數據結構的特點是:

  1. 大多數為二叉樹,也可以多叉樹,無論是幾叉樹,它都具有樹結構的所有特點
  2. 葉子節點value是數據集合的單元數據或者單元數據Hash
  3. 非葉子節點的value是根據它下面所有的葉子節點值,然後按照Hash算法計算而得出


區塊鏈與你“最熟悉的陌生人”


近年來,除了Bitcoin、Ethereum、IPFS,一大批計算機工程突破,都得益於這種數據結構進行完整性校驗,例如文件系統ZFS、Btrfs,另一種分佈式版本控制系統Mercurial,NoSQL數據庫Apache Cassandra、Riak、Dynamo等。BT下載,也是通過默克爾樹進行完整性校驗。

要實現完整性校驗,最簡單的方法是對整個數據文件做Hash運算,把得到的Hash值公佈在網上,下載數據後,再次運算Hash值,如果運算結果相等,就表示沒有任何的損壞。

假如從穩定的服務器上下載,那麼採用單個Hash來進行校驗的形式是可以接受的。但在點對點網絡中作數據傳輸時,會從同時從多個機器上下載,且線路充斥著不穩定,這時需要有更加巧妙的做法。

實際中,都是把比較大的一個文件,切成小塊。如果有一個小塊數據在傳輸過程中損壞,只要重新下載這一個數據塊就行。當然這就要求每個數據塊都擁有自己的Hash值。

以我們熟悉的BT下載為例,下載真正的數據之前,會先下載一個Hash列表的。這時有一個問題出現——那麼多的Hash,怎麼保證它們本身都是正確地呢?

答案是需要一個“根Hash”。把每個小塊的Hash值拼到一起,然後對整個這個長長的字符串再做一次Hash運算,最終的結果就是Hash列表的根Hash。於是,如果我們能夠保證從一個絕對可信的網站,或者從我們的朋友手裡拿到一個正確的根Hash,就可以用它來校驗Hash列表中的每一個Hash都是正確的,進而可以保證下載的每一個數據塊的正確性了。

這種設想挺好,但實際應用中,還有不足,這就是為什麼要發默克爾樹。

在最底層,與Hash列表一樣,數據被分成小塊,有相應的Hash和其對應。但是往上走,並不是直接去運算根Hash,而是把相鄰的兩個Hash合併成一個字符串,然後運算這個字符串的Hash,這樣每兩個Hash就結婚生子,得到了一個“子Hash”。

如果最底層的Hash總數是單數,那到最後必然出現一個單身Hash,這種情況就直接對它進行Hash運算,所以也能得到它的子Hash。於是往上推,依然是一樣的方式,可以得到數目更少的新一級Hash,最終必然形成一棵倒掛的樹,到了樹根的這個位置,這一代就剩下一個根Hash了,稱為默克爾根。

相對於Hash List,Merkle Tree 的明顯的一個好處是可以單獨拿出一個分支來(作為一個小樹)對部分數據進行校驗,這個很多使用場合就帶來了Hash列表所不能比擬的方便和高效。


分享到:


相關文章: