15、以太坊中的狀態(tài)樹(shù)

以太坊采用基于賬戶(hù)的模式神年,系統(tǒng)中顯式地維護(hù)每個(gè)賬戶(hù)上有多少余額已维,今天看一下用什么樣的數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)account-based ledger

要完成的功能:

從 賬戶(hù)地址 到 賬戶(hù)狀態(tài) 的映射

addr -> state

addr:賬戶(hù)地址,以太坊中用的賬戶(hù)地址是160位瘤袖,也就是20個(gè)字節(jié)衣摩,一般表示成40個(gè)十六進(jìn)制的數(shù)(0~f)。

state:外部賬戶(hù)和合約賬戶(hù)的狀態(tài)捂敌,包括余額艾扮、交易次數(shù)、合約賬戶(hù)還包括代碼占婉、存儲(chǔ)泡嘴。

那么要設(shè)計(jì)什么樣的數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)這個(gè)映射呢?


數(shù)據(jù)結(jié)構(gòu)假設(shè)

以下是幾個(gè)思考的方案:

假設(shè)方案一:用哈希表實(shí)現(xiàn)

直觀上看逆济,像一個(gè)很典型的key-value pair酌予,給出一個(gè)賬戶(hù)地址,要找到相應(yīng)的賬戶(hù)狀態(tài)奖慌。

所以一個(gè)直觀的想法:用哈希表實(shí)現(xiàn)抛虫。

系統(tǒng)中的全節(jié)點(diǎn)維護(hù)一個(gè)哈希表,每次有一個(gè)新的賬戶(hù)简僧,插入到哈希表里面建椰,查詢(xún)賬戶(hù)的余額,就直接在哈希表中查詢(xún)岛马。如果不考慮哈希碰撞的話(huà)棉姐,基本上查詢(xún)的效率是常數(shù)時(shí)間內(nèi)完成的屠列,更新也是很容易在哈希表中更新的。

>>> 問(wèn)題:如果用這個(gè)哈希表要提供merkle proof怎么提供伞矩?

比如說(shuō)你要跟一個(gè)人簽合同笛洛,希望他能證明一下他有多少錢(qián),怎么提供證明呢乃坤?

一種方法是 把哈希表中的元素組織成一個(gè)Merkle tree苛让,然后算出一個(gè)根哈希值,這個(gè)根哈希值存在block header里侥袜,只要根哈希值是正確的蝌诡,就能保證底下的樹(shù)不會(huì)被篡改。

如果有新區(qū)塊發(fā)布怎么辦枫吧?

新區(qū)塊中包含新的交易,執(zhí)行這個(gè)交易必然會(huì)使哈希表的內(nèi)容發(fā)生變化宇色,發(fā)布下一個(gè)區(qū)塊的時(shí)候九杂,再重新把哈希表中的內(nèi)容組織成一個(gè)Merkle tree嗎?

這個(gè)代價(jià)太大了宣蠕。實(shí)際上例隆,真正發(fā)生變化的賬戶(hù)狀態(tài)只是一小部分,因?yàn)橹挥心莻€(gè)區(qū)塊里所關(guān)聯(lián)的賬戶(hù)才會(huì)發(fā)生變化抢蚀,大多數(shù)賬戶(hù)的狀態(tài)是不變的镀层。所以每次你都重新構(gòu)造一次Merkle tree,這個(gè)代價(jià)是很大的皿曲。

比特幣系統(tǒng)當(dāng)中難道不是每出現(xiàn)一個(gè)區(qū)塊也要重新構(gòu)造一個(gè)Merkle tree嗎唱逢?那個(gè)為什么沒(méi)有問(wèn)題?

那個(gè)Merkle tree是把區(qū)塊里包含的交易組織成一個(gè)Merkle tree屋休,那區(qū)塊中的交易每次發(fā)布一個(gè)新的區(qū)塊又有一系列新的交易坞古,所以比特幣中的Merkle tree是immutable的,每次發(fā)布一個(gè)新的區(qū)塊對(duì)應(yīng)一個(gè)Merkle tree劫樟,然后這棵Merkle tree構(gòu)建完之后是不會(huì)再改的痪枫,下次再發(fā)布一個(gè)新的區(qū)塊再構(gòu)建一個(gè)新的Merkle tree。

那區(qū)塊里有多少個(gè)交易呢叠艳?最多差不多4000個(gè)(按照1M字節(jié)奶陈,每個(gè)交易大概是250M字節(jié)左右),這個(gè)其實(shí)是一個(gè)上限附较,很多區(qū)塊的交易數(shù)目根本到不了4000個(gè)吃粒,有好多區(qū)塊就只有幾百個(gè),甚至有可能還有更少的翅睛,所以每次發(fā)布一個(gè)區(qū)塊声搁,比特幣里構(gòu)建一個(gè)Merkle tree黑竞,是要把這幾百個(gè)到幾千個(gè)交易構(gòu)成一個(gè)Merkle tree。

這里如果采用這種方法會(huì)是什么情況疏旨?

是要把所有的以太坊賬戶(hù)一起構(gòu)成一個(gè)Merkle tree很魂,這個(gè)就比剛才講的幾百,幾千個(gè)交易要高出好幾個(gè)數(shù)量級(jí)檐涝,相當(dāng)于每次發(fā)布一個(gè)區(qū)塊要把所有的賬戶(hù)遍歷一遍構(gòu)建出一個(gè)Merkle tree遏匆,下次再有一個(gè)區(qū)塊,再把所有的賬戶(hù)遍歷一遍谁榜,再構(gòu)建出一個(gè)Merkle tree幅聘。

除了提供Merkle proof證明賬戶(hù)有多少錢(qián)之外,這個(gè)Merkle tree還有另外一個(gè)很重要的作用窃植,就是維護(hù)各個(gè)全節(jié)點(diǎn)之間狀態(tài)的一致性帝蒿,如果沒(méi)有根哈希值不去發(fā)不出來(lái),每個(gè)節(jié)點(diǎn)就是在本地維護(hù)一個(gè)數(shù)據(jù)結(jié)構(gòu)巷怜,那怎么知道你的數(shù)據(jù)結(jié)構(gòu)的狀態(tài)跟別人的數(shù)據(jù)結(jié)構(gòu)的狀態(tài)是不是一致呢葛超,要各個(gè)全節(jié)點(diǎn)保持狀態(tài)的一致才行。這也是為什么比特幣中把根哈希值寫(xiě)在塊頭里的原因延塑,就是對(duì)于當(dāng)前區(qū)塊中包含哪些交易绣张,所有的全節(jié)點(diǎn)要有一個(gè)共識(shí)。

>>> 結(jié)論:不可行关带,因?yàn)槊看螛?gòu)建Merkle tree的代價(jià)太大

如果每個(gè)全節(jié)點(diǎn)在本地維護(hù)一個(gè)哈希表侥涵,然后需要構(gòu)建Merkle tree的時(shí)候構(gòu)建出Merkle tree來(lái),然后根哈希值發(fā)到區(qū)塊頭里宋雏,這個(gè)方法是不行的芜飘。哈希表本身的效率是挺好的,插入好芭,更改效率都很好燃箭,但是每次構(gòu)建Merkle tree的代價(jià)太大了


假設(shè)方案二:直接用一個(gè)Merkle tree把所有的賬戶(hù)都放進(jìn)去

不要哈希表了舍败,直接用一個(gè)Merkle tree把所有的賬戶(hù)都放進(jìn)去招狸,要改的時(shí)候直接在Merkle tree里改。因?yàn)槊總€(gè)區(qū)塊更新的只是一小部分賬戶(hù)邻薯,所以改的時(shí)候只是Merkle tree里的一小部分裙戏。

>>> 問(wèn)題1:Merkle tree沒(méi)有提供一個(gè)高效的查找、更新的方法

比特幣的Merkle tree是怎么構(gòu)建的厕诡?

就最底下一層是tx累榜,然后哈希值放到上面節(jié)點(diǎn)里,兩兩結(jié)合,然后再取一個(gè)哈希往上整壹罚。

他沒(méi)有提供一個(gè)快速查找葛作,更新的方法。

>>> 問(wèn)題2:直接把賬戶(hù)放到Merkle tree里猖凛,這個(gè)Merkle tree要不要排序赂蠢?

Sorted Merkle tree

如果不排序會(huì)怎么樣?

1辨泳、查找速度會(huì)慢虱岂。

2、還有一個(gè)問(wèn)題菠红,這些賬戶(hù)組成了這棵merkle tree第岖,葉節(jié)點(diǎn)是這些賬戶(hù)的信息,如果不規(guī)定這些賬戶(hù)在葉節(jié)點(diǎn)出現(xiàn)的順序试溯,那么這樣構(gòu)建出來(lái)的Merkle tree不是唯一的蔑滓。

比如,系統(tǒng)中有很多全節(jié)點(diǎn)耍共,每個(gè)全節(jié)點(diǎn)按照自己的某個(gè)順序烫饼,比如說(shuō)他聽(tīng)到某個(gè)交易的順序構(gòu)建一個(gè)Merkle tree,那么葉結(jié)點(diǎn)的順序是亂的试读,每個(gè)節(jié)點(diǎn)都是自己決定的,最后構(gòu)建出的Merkle tree是不一樣的荠耽,算出的根哈希值也是不一樣的

比特幣中的節(jié)點(diǎn)也是不排序的钩骇,那為什么比特幣就沒(méi)有問(wèn)題呢?

因?yàn)楸忍貛胖械拿總€(gè)全節(jié)點(diǎn)收到的交易的順序也是不一樣的铝量,理論上說(shuō)構(gòu)建的Merkle tree的根哈希值也是不一樣的倘屹。

但是比特幣中,每個(gè)節(jié)點(diǎn)在本地組裝一個(gè)候選區(qū)塊慢叨,這個(gè)節(jié)點(diǎn)自己決定哪些交易纽匙、以什么順序順序打包進(jìn)這個(gè)區(qū)塊里,然后通過(guò)挖礦去競(jìng)爭(zhēng)記賬權(quán)拍谐。如果他沒(méi)有搶到記賬權(quán)烛缔,他的任何決定其他人沒(méi)必要知道;只有他有記賬權(quán)轩拨,且發(fā)布區(qū)塊后最終成為被大家接受的區(qū)塊践瓷,那么,這個(gè)順序就是發(fā)布這個(gè)區(qū)塊的節(jié)點(diǎn)確定的亡蓉。

也就是說(shuō)晕翠,比特幣中雖然也沒(méi)用排序的Merkle tree,但是他那個(gè)順序是唯一的砍濒,是由發(fā)布區(qū)塊的那個(gè)節(jié)點(diǎn)確定的淋肾。

那為什么以太坊不能這樣做硫麻?

如果以太坊也這么做的話(huà),需要把賬戶(hù)的狀態(tài)發(fā)布到區(qū)塊里樊卓。也可以說(shuō)是 每個(gè)全節(jié)點(diǎn)自己決定 怎么把賬戶(hù)組織成一個(gè)Merkle tree拿愧,算出跟哈希值、挖出礦简识,但要怎么讓別人知道這個(gè)順序赶掖,你得把他發(fā)布到區(qū)塊里。但發(fā)布的是所有賬戶(hù)的狀態(tài)七扰,不是交易奢赂,這兩者差好幾個(gè)數(shù)量級(jí),比特幣發(fā)布一個(gè)區(qū)塊只需要幾百個(gè)/幾千個(gè)交易颈走。

>>> 結(jié)論:不可行膳灶,不排序的Merkle tree是不行的。

交易是必須要發(fā)布的立由,不發(fā)布別人就沒(méi)法知道轧钓,但賬戶(hù)狀態(tài)可以維護(hù)在本地,而且大部分賬戶(hù)狀態(tài)是不變的锐膜。一個(gè)區(qū)塊里的交易只能改很少的賬戶(hù)毕箍,大多數(shù)賬戶(hù)是不變的,而且重復(fù)發(fā)布道盏,每隔十幾秒發(fā)布一個(gè)新的區(qū)塊而柑,把所有狀態(tài)都打包發(fā)布一遍,下次再過(guò)十幾秒再發(fā)布一遍荷逞,這個(gè)是不可行媒咳。


假設(shè)方案三:用Sorted Merkle tree

>>> 問(wèn)題:新增一個(gè)賬戶(hù)怎么辦

產(chǎn)生一個(gè)賬戶(hù)的地址是隨機(jī)的,他的葉節(jié)點(diǎn)的位置很可能是插在中間的种远,那后面這些樹(shù)的結(jié)構(gòu)都得變涩澡。

新產(chǎn)生一個(gè)賬戶(hù),對(duì)外發(fā)生了交互坠敷,我需要把他加入到我的數(shù)據(jù)結(jié)構(gòu)里妙同,這是沒(méi)錯(cuò)的,但問(wèn)題是常拓,這個(gè)加入的代價(jià)有多大渐溶?

可能大半棵Merkle tree需要重構(gòu),這個(gè)代價(jià)太大了弄抬。

>>> 結(jié)論:不可行茎辐,用Sorted Merkle tree,插入、刪除代價(jià)都太大拖陆。

而且弛槐,區(qū)塊鏈?zhǔn)遣豢纱鄹牡模钦f(shuō)添東西容易依啰,刪東西難乎串。

其實(shí)以太坊中沒(méi)有顯式地刪除賬戶(hù)的操作,有的賬戶(hù)上就一點(diǎn)錢(qián)速警,就一兩個(gè)Wei叹誉,你也不能把他刪掉。


上面的方案都不行闷旧。


以太坊的數(shù)據(jù)結(jié)構(gòu)

那么以太坊中采用的方法是用一個(gè)叫MPT(Merkle Patricia Tree)的結(jié)構(gòu)长豁。

講這個(gè)之前先講一個(gè)簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)。

【Trie】

來(lái)源于“retrieval”忙灼,檢索匠襟。

trie:字典樹(shù),前綴樹(shù)该园。

也是一種key-value的數(shù)據(jù)對(duì)酸舍,一般來(lái)說(shuō)key用字符串用的比較多,比如說(shuō)一些單詞排成一個(gè)trie的數(shù)據(jù)結(jié)構(gòu)里初。

舉例:

general

genesis(區(qū)塊鏈的第一個(gè)區(qū)塊叫做genesis block,創(chuàng)世紀(jì)塊)

go

god

good

下圖就是一個(gè)trie的結(jié)構(gòu)啃勉,這幾個(gè)單詞都是以G開(kāi)頭的,然后第二個(gè)字母就開(kāi)始分裂了双妨,左邊是E璧亮,右邊是O,左邊這前兩個(gè)單詞都是N和E斥难,然后下面再分開(kāi),R和S帘饶,然后是后三個(gè)字母哑诊,右邊這個(gè)分支,O這個(gè)分支及刻,Go就已經(jīng)結(jié)束了镀裤,從這個(gè)可以看到單詞可能在trie的中間節(jié)點(diǎn)結(jié)束,然后左邊是D缴饭,右邊是O暑劝,左邊變成了God,右邊下來(lái)是Good颗搂。

Trie樹(shù)可以利用字符串公共前綴來(lái)節(jié)約存儲(chǔ)空間担猛。

如果系統(tǒng)中存在大量字符串且這些字符串基本沒(méi)有公共前綴,則相應(yīng)的trie樹(shù)將非常消耗內(nèi)存,這也是trie樹(shù)的一個(gè)缺點(diǎn)傅联。

Trie的特點(diǎn)

特點(diǎn)一:trie的每個(gè)節(jié)點(diǎn)的分叉數(shù)目先改,取決于 key值里 每個(gè)元素的 取值范圍

這個(gè)例子當(dāng)中,每個(gè)都是英文單詞蒸走,而且是小寫(xiě)的仇奶,所以每個(gè)節(jié)點(diǎn)的分叉數(shù)目最多是26個(gè),加上一個(gè)結(jié)束標(biāo)志位(表示這個(gè)單詞到這個(gè)地方就結(jié)束了)比驻。

英文字母的字典樹(shù)是一個(gè)26叉樹(shù)该溯,數(shù)字的字典樹(shù)是一個(gè)10叉樹(shù)。

在以太坊中地址是表示成40個(gè)十六進(jìn)制的數(shù)别惦,因此分叉數(shù)目(branching factor)是17(十六進(jìn)制的0~f狈茉,再加上結(jié)束標(biāo)志位)。


特點(diǎn)二:trie的查找速率步咪,取決于key的長(zhǎng)度

鍵值越長(zhǎng)论皆,查找需要訪(fǎng)問(wèn)內(nèi)存的次數(shù)就越多。

在這個(gè)例子當(dāng)中猾漫,不同的單詞鍵值長(zhǎng)度是不同的点晴。

在以太坊中,所有鍵值都是40悯周,因?yàn)榈刂范际?0位十六進(jìn)制的數(shù)粒督。

比特幣和以太坊的地址是不通用的,兩個(gè)地址的格式長(zhǎng)度都是不一樣的禽翼。

但有一點(diǎn)是類(lèi)似的屠橄,以太坊中的地址也是公鑰經(jīng)過(guò)轉(zhuǎn)換得來(lái)的。其實(shí)就是公鑰取哈希闰挡,然后前面的不要锐墙,只要后面這部分,就得到一個(gè)160bit的地址长酗。


特點(diǎn)三:只要兩個(gè)地址不一樣溪北,最后肯定映射到樹(shù)中的不同分支,所以trie是不會(huì)出現(xiàn)碰撞的


特點(diǎn)四:不同的節(jié)點(diǎn)夺脾,不論按照什么順序插入這些賬戶(hù)之拨,最后構(gòu)造出來(lái)的樹(shù)是一樣的

前面講Merkle tree,如果不排序的話(huà)咧叭,一個(gè)問(wèn)題是賬戶(hù)插入到Merkle tree 的順序不一樣蚀乔,得到的樹(shù)的結(jié)構(gòu)也不一樣。

那trie呢菲茬?

比如上圖中的這五個(gè)單詞吉挣,換一個(gè)順序插到這個(gè)樹(shù)里面派撕,得到的是一個(gè)不同的樹(shù)嗎?其實(shí)是一樣的听想,只要給定的輸入不變腥刹,無(wú)論輸入怎么打亂重排,最后插入到trie當(dāng)中汉买,得到的樹(shù)是一樣的衔峰。


特點(diǎn)五:更新操作的局部性很好

每次發(fā)布一個(gè)區(qū)塊,系統(tǒng)中絕大多數(shù)賬戶(hù)的狀態(tài)是不變的蛙粘,只有個(gè)別受到影響的賬戶(hù)才會(huì)變垫卤,所以更新操作的局部性很重要

trie局部性呢出牧?

比如在上圖中穴肘,我要更新genesis這個(gè)key對(duì)應(yīng)的value(這個(gè)圖當(dāng)中只畫(huà)出了key,沒(méi)有畫(huà)出value)舔痕,只要訪(fǎng)問(wèn)genesis的那個(gè)分支评抚,其他分支不用訪(fǎng)問(wèn)的,也不用遍歷整棵樹(shù)伯复。


Tire的缺點(diǎn):存儲(chǔ)浪費(fèi)

像左邊分支都只有一個(gè)子節(jié)點(diǎn)慨代,對(duì)于這種一脈單傳的情況,如果能把節(jié)點(diǎn)進(jìn)行合并啸如,那么可以減小存儲(chǔ)的開(kāi)銷(xiāo)侍匙,同時(shí)也提高了查找的效率,不用一次一個(gè)一個(gè)的往下找了叮雳。


那么就引入了Patricia tree想暗,也有人寫(xiě)成Patricia trie,是經(jīng)過(guò)路徑壓縮的前綴樹(shù)帘不,有時(shí)候也叫壓縮前綴樹(shù)说莫。


【Patricia tree】/【Patricia trie】

這個(gè)如果進(jìn)行路徑壓縮就變成下圖的樣子。

可以看到寞焙,G下面還是E和O進(jìn)行分叉唬滑,E下面之后跟的都是NE,再往下就是E和S分叉棺弊,然后后面都和在一起了,右邊都是一樣的擒悬。

這樣壓縮之后有什么好處模她?

直觀上看,這個(gè)高度明顯縮短了懂牧,訪(fǎng)問(wèn)內(nèi)存的次數(shù)會(huì)大大減少侈净,效率提高了尊勿。

注意:對(duì)于Patricia tree來(lái)說(shuō),新插入一個(gè)單詞畜侦,原來(lái)壓縮的路徑可能需要擴(kuò)展開(kāi)元扔。

比如這個(gè)例子當(dāng)中,加入geometry旋膳,左邊的分支就不能那樣壓縮了澎语。


路徑壓縮在什么情況下效果比較好?

鍵值分布比較稀疏的時(shí)候验懊,路徑壓縮效果比較好擅羞。

比如說(shuō),這個(gè)例子當(dāng)中是用英文單詞义图,假設(shè)每個(gè)單詞都很長(zhǎng)减俏,但是一共沒(méi)有幾個(gè)單詞,

舉個(gè)例子:

misunderstanding

decentralization(去中心化)

disintermediation(去中間商碱工,非中介化)(intermediaries:中間商)

這三個(gè)單詞插入到一個(gè)普通的trie里面就成了下圖的樣子

可以看到這樣的結(jié)構(gòu)效率是比較低的娃承,基本上是一條線(xiàn)了。

如果用Patricia tree的話(huà)怕篷,參考下圖

這個(gè)樹(shù)的高度明顯改善多了历筝。

所以鍵值分布比較稀疏的時(shí)候苔货,路徑壓縮效果比較好舱禽。

以太坊應(yīng)用中鍵值是不是稀疏呢?

以太坊應(yīng)用中鍵值是地址屋匕,地址是160位的蹂析,地址空間有2^160舔示,這是一個(gè)非常非常大的數(shù)。

如果你設(shè)計(jì)一個(gè)計(jì)算機(jī)程序的算法电抚,他需要進(jìn)行運(yùn)算的次數(shù)是2^160惕稻,那這個(gè)在我們所有人的有生之年都不可能算出來(lái),全世界的以太坊的賬戶(hù)數(shù)目加在一起也遠(yuǎn)遠(yuǎn)沒(méi)有這么大蝙叛,跟這個(gè)數(shù)比俺祠,是微乎其微的。

為什么要弄這么稀疏借帘,不把地址長(zhǎng)度縮短一點(diǎn)蜘渣,這樣訪(fǎng)問(wèn)效率也快,也沒(méi)必要那么稀疏了肺然?

以太坊中普通賬戶(hù)跟比特幣的創(chuàng)建方法是一樣的蔫缸,沒(méi)有一個(gè)中央的節(jié)點(diǎn),就每個(gè)用戶(hù)獨(dú)立創(chuàng)建賬戶(hù)际起。你在本地產(chǎn)生一個(gè)公私鑰對(duì)拾碌,就是一個(gè)賬戶(hù)吐葱。

那怎么防止兩個(gè)人的賬戶(hù)碰撞,產(chǎn)生的一樣呢校翔?

這種可能性是存在的弟跑,但是這個(gè)概率比地球爆炸還要小。

怎么達(dá)到這么小的概率防症,就是地址要足夠長(zhǎng)孟辑,分布足夠稀疏,才不會(huì)產(chǎn)生碰撞告希。

這個(gè)可能看上去有點(diǎn)浪費(fèi)扑浸,但是這是去中心化的系統(tǒng)防止賬戶(hù)沖突的唯一辦法

所以他是非常稀疏的燕偶,這就是為什么在數(shù)據(jù)結(jié)構(gòu)中喝噪,要用Patricia tree


【MPT】(Merkle Patricia tree)

>>> Merkle tree和Binary tree的區(qū)別

即區(qū)塊鏈與普通鏈表的區(qū)別:把普通指針換成了哈希指針指么。

>>> Merkle Patricia tree和Patricia tree的區(qū)別

也是 把普通指針換成了哈希指針酝惧。

所有的賬戶(hù)組織成一個(gè)Patricia tree,用路徑壓縮提高效率伯诬,然后把普通指針換成哈希指針晚唇,所以就可以計(jì)算出一個(gè)根哈希值。這個(gè)跟哈希值也是寫(xiě)在block header里面盗似。

比特幣的block header里只有一個(gè)根哈希值:交易樹(shù)哩陕,就是區(qū)塊里包含的交易組成的Merkle tree組成的根哈希值。

以太坊的block header里有三個(gè)根哈希值:交易樹(shù)赫舒、狀態(tài)樹(shù)悍及、收據(jù)樹(shù)。

現(xiàn)在講的是狀態(tài)樹(shù)接癌,賬戶(hù)狀態(tài)最后組織成了一個(gè)Merkle tree心赶,他的根哈希值。

這個(gè)根哈希值的作用:

1缺猛、防止篡改

只要根哈希值不變缨叫,整個(gè)樹(shù)的任何部分都沒(méi)有辦法被篡改,也就是說(shuō)每個(gè)賬戶(hù)的狀態(tài)都能保證他是沒(méi)有被篡改過(guò)的荔燎。

2耻姥、Merkle proof

(1)能證明賬戶(hù)的余額是多少

你這個(gè)賬戶(hù)所在的分支自己向上作為Merkle proof發(fā)給輕節(jié)點(diǎn),輕節(jié)點(diǎn)可以驗(yàn)證你的賬戶(hù)上有多少錢(qián)有咨。

(2)能證明某個(gè)賬戶(hù)是不存在的

之前講過(guò)咏闪,Sorted Merkle tree的一個(gè)作用是能證明non-membership。

這里的證明方法跟Sorted Merkle tree類(lèi)似摔吏。

比如鸽嫂,給一個(gè)地址轉(zhuǎn)賬之前,驗(yàn)證一下全節(jié)點(diǎn)里有沒(méi)有這個(gè)賬戶(hù)信息征讲。說(shuō)的更直白一點(diǎn)据某,證明MPT中某個(gè)鍵值是不存在的

如果存在的話(huà)诗箍,是在什么樣的分支癣籽,把這個(gè)分支作為Merkle proof發(fā)過(guò)去,可以證明他是不存在的滤祖。


Modified MPT

以太坊中用到的不是原生的MPT筷狼,是Modified MPT,就是對(duì)MPT做一些修改匠童,這些修改不是很本質(zhì)的修改埂材。

舉個(gè)例子(如下圖),有四個(gè)賬戶(hù)(右上角)汤求,為了簡(jiǎn)單起見(jiàn)俏险,地址都比較短,假設(shè)只有7位的地址扬绪,而不是40位竖独,賬戶(hù)狀態(tài)也只顯示出了余額,其他賬戶(hù)狀態(tài)沒(méi)有顯示出來(lái)挤牛。第一個(gè)賬戶(hù)有45個(gè)以太幣莹痢,第二個(gè)賬戶(hù)只有1WEI(這個(gè)是以太坊中最小的計(jì)量單位,1WEI基本上可以忽略不計(jì))

這個(gè)例子當(dāng)中墓赴,節(jié)點(diǎn)分為三種竞膳。

1、Extension Node(擴(kuò)展節(jié)點(diǎn))

如果這個(gè)樹(shù)中出現(xiàn)了路徑壓縮就會(huì)有一個(gè)Extension Node竣蹦。

這四個(gè)地址前兩位都是一樣的a7顶猜,所以他的Root(根節(jié)點(diǎn))就是一個(gè)Extension Node

shared nibble(nibble:16進(jìn)制數(shù)痘括,一個(gè)nibble就是一個(gè)16進(jìn)制數(shù))长窄,這里共享的nibble是a7

2、Branch Node(分支節(jié)點(diǎn))

這里第三位就分開(kāi)了纲菌,有1,7,f挠日,所以就跟了一個(gè)Branch Node

3翰舌、Leaf Node(葉節(jié)點(diǎn))

先說(shuō)1嚣潜,這個(gè)1之后就是1355,只有這一個(gè)地址椅贱,就跟了Leaf Node懂算。

這個(gè)7有兩個(gè)地址只冻,連著路徑壓縮d3。

然后再往下3和9分開(kāi)了计技,跟著一個(gè)Branch Node喜德。

下面兩個(gè)Leaf Node,都是7垮媒。

最后一個(gè)f舍悯,就跟著一個(gè)Leaf Node,9365睡雇。

這就是狀態(tài)樹(shù)萌衬。

另外,這個(gè)樹(shù)的根節(jié)點(diǎn)取哈希之后得到的一個(gè)根哈希值它抱,是要寫(xiě)在塊頭里的(左上角)秕豫。

他用的也是哈希指針。比如7這個(gè)位置抗愁,這里存的是下面這個(gè)節(jié)點(diǎn)(extension node)的哈希值馁蒂。如果是普通指針的話(huà),7這個(gè)位置存的是下面這個(gè)節(jié)點(diǎn)的地址蜘腌。

每次發(fā)布一個(gè)新的區(qū)塊的時(shí)候沫屡,這個(gè)狀態(tài)樹(shù)中,有一些節(jié)點(diǎn)的值會(huì)發(fā)生變化撮珠,這些改變不是在原地改沮脖,而是新建一些分支,原來(lái)的狀態(tài)其實(shí)是保留下來(lái)的芯急。


下面這個(gè)例子當(dāng)中勺届,有兩個(gè)相鄰的區(qū)塊。

Block N Header:State Root就是狀態(tài)樹(shù)的根哈希值娶耍,下面(灰色)顯示的是這棵狀態(tài)樹(shù)免姿。

Block N+1 Header:這個(gè)是新的區(qū)塊的狀態(tài)樹(shù)

可以看到榕酒,雖然每一個(gè)區(qū)塊都有一個(gè)狀態(tài)樹(shù)胚膊,但是這兩顆樹(shù)的大部分節(jié)點(diǎn)是共享的

右邊這棵樹(shù)主要都是指向左邊這顆樹(shù)的節(jié)點(diǎn)想鹰,只有那些發(fā)生改變的節(jié)點(diǎn)是需要新建一個(gè)分支紊婉。

這個(gè)例子中,這個(gè)賬戶(hù)是一個(gè)合約賬戶(hù)辑舷,因?yàn)橛蠧ode喻犁,還有Storage。

合約賬戶(hù)的存儲(chǔ)也是由MPT保存下來(lái)的。

這個(gè)存儲(chǔ)其實(shí)也是一個(gè)Key Value Store肢础,維護(hù)的是從這個(gè)變量到這個(gè)變量取值的一個(gè)映射还栓,在以太坊當(dāng)中,也是用的一棵MPT传轰。

所以以太坊中的這個(gè)結(jié)構(gòu)是一個(gè)大的MPT蝙云,包含很多小的MPT每一個(gè)合約賬戶(hù)的存儲(chǔ)都是一棵小的MPT路召。

上圖中這個(gè)賬戶(hù)的新的區(qū)塊里:

Nonce發(fā)生了變化。

Balance余額也發(fā)生了變化波材。

Code不變的股淡,所以Codehash指向原來(lái)樹(shù)中那個(gè)節(jié)點(diǎn)。

Storage變了(存儲(chǔ)下面這個(gè)叫存儲(chǔ)樹(shù))廷区,在存儲(chǔ)樹(shù)中唯灵,大部分節(jié)點(diǎn)也是沒(méi)有改變。這個(gè)例子當(dāng)中隙轻,只有一個(gè)節(jié)點(diǎn)變了埠帕,這個(gè)整數(shù)變量從29變成了45,所以新建了一個(gè)分支玖绿。

所以敛瓷,系統(tǒng)中每個(gè)全節(jié)點(diǎn)需要維護(hù)的不是一棵MPT,而是每次出現(xiàn)一個(gè)區(qū)塊斑匪,都要新建一個(gè)MPT呐籽,只不過(guò)這些狀態(tài)樹(shù)中,大部分的節(jié)點(diǎn)是共享的蚀瘸,只有少部分發(fā)生變化的節(jié)點(diǎn)要新建分支狡蝶。

問(wèn):為什么要保留歷史狀態(tài),為什么不在原地直接改了贮勃?

系統(tǒng)當(dāng)中有時(shí)候會(huì)出現(xiàn)分叉贪惹,臨時(shí)性的分叉是很普遍的。

以太坊把出塊時(shí)間降低到十幾秒之后寂嘉,這種臨時(shí)性的分叉是常態(tài)奏瞬,因?yàn)閰^(qū)塊在網(wǎng)上傳播時(shí)間可能也需要十幾秒。

假設(shè)由有個(gè)分叉垫释,這兩個(gè)節(jié)點(diǎn)同時(shí)獲得記賬權(quán)(如下圖)丝格。

這兩個(gè)分叉最終上面那個(gè)勝出了,下面這個(gè)分叉的節(jié)點(diǎn)該怎么辦棵譬?

這個(gè)時(shí)候就要回滾(roll back)显蝌,就這個(gè)節(jié)點(diǎn)當(dāng)前的狀態(tài),就接受了下面這個(gè)節(jié)點(diǎn)的狀態(tài)要取消掉,退回到上一個(gè)節(jié)點(diǎn)的狀態(tài)曼尊,然后沿著上面那條鏈往下推進(jìn)酬诀。

有時(shí)候可能要把當(dāng)前狀態(tài)退回到?jīng)]有處理到這個(gè)區(qū)塊中交易的前一個(gè)狀態(tài)。

那怎么實(shí)現(xiàn)回滾呢骆撇?

就是要維護(hù)這些歷史紀(jì)錄瞒御。

這個(gè)跟比特幣還不太一樣,如果是比特幣的話(huà)神郊,他的交易類(lèi)型比較簡(jiǎn)單肴裙,有的時(shí)候可以通過(guò)這種反向操作推算出前一個(gè)狀態(tài)。

比如涌乳,一個(gè)簡(jiǎn)單的轉(zhuǎn)賬交易(如下圖)蜻懦,A轉(zhuǎn)給B10個(gè)BTC,這個(gè)對(duì)賬戶(hù)余額有什么影響呢夕晓?

A的賬戶(hù)上少了10個(gè)比特幣宛乃,B的狀態(tài)多了10個(gè)比特幣。

假如這個(gè)狀態(tài)要回滾了蒸辆,退回到前一個(gè)狀態(tài)征炼,那就把B這個(gè)賬戶(hù)減少10個(gè)比特幣,把A這個(gè)賬戶(hù)加回去10個(gè)比特幣就行了躬贡。

簡(jiǎn)單的轉(zhuǎn)賬交易回滾其實(shí)是比較容易的谆奥。

以太坊中為什么不行?

因?yàn)橐蕴恢杏?b>智能合約逗宜。

智能合約是圖靈完備的雄右,編程功能是很強(qiáng)的。從理論上說(shuō)纺讲,可以實(shí)現(xiàn)很復(fù)雜的功能擂仍,跟比特幣簡(jiǎn)單的腳本還不太一樣。

所以以太坊中如果不保存前面的狀態(tài)熬甚,智能合約執(zhí)行完之后逢渔,想再推算出前面是什么狀態(tài),這是不可能的乡括,所以要想支持回滾肃廓,必須保存歷史狀態(tài)


以太坊中代碼的數(shù)據(jù)結(jié)構(gòu)

1诲泌、block header的結(jié)構(gòu)

ParentHash:父區(qū)塊的哈希值盲赊。是區(qū)塊鏈中前一個(gè)區(qū)塊的哈希值,不是區(qū)塊的塊頭的哈希值敷扫。

UncleHash:叔父區(qū)塊的哈希值哀蘑。后面講Ghost協(xié)議的時(shí)候,每個(gè)區(qū)塊還有叔父區(qū)塊。而且后面會(huì)看到绘迁,比較奇怪的一點(diǎn)是合溺,大多數(shù)人表面上看ParentHash,UncleHash是一個(gè)輩分的缀台,但是以太坊中不是這樣的棠赛,這個(gè)Uncle比Parent可能大好多輩分。

Coinbase:是挖出這個(gè)礦的這個(gè)區(qū)塊的礦工的地址膛腐。

--------- 下面這三個(gè)就是本講相關(guān)的三棵樹(shù)的根哈希睛约,以太坊中有三棵樹(shù):狀態(tài)樹(shù),交易數(shù)和收據(jù)樹(shù)哲身。

Root:狀態(tài)樹(shù)的根哈希值痰腮。

TxHash:交易樹(shù)的根哈希值(類(lèi)似比特幣中的那個(gè)根哈希值)。

ReceiptHash:收據(jù)樹(shù)的根哈希值律罢。

Bloom:提供一個(gè)高效的查詢(xún),符合某種條件的交易的執(zhí)行結(jié)果(跟收據(jù)樹(shù)是相關(guān)的)棍丐。

Diffculty:挖礦難度(要根據(jù)需要調(diào)整)

--------- GasLimit和GasUsed是跟汽油費(fèi)相關(guān)的误辑,智能合約要消耗汽油費(fèi),類(lèi)似于比特幣中的交易費(fèi)歌逢。

GasLimit:單個(gè)區(qū)塊允許的最多gas總量

GasUsed:該交易消耗的總gas數(shù)量

Time:區(qū)塊的大致的產(chǎn)生時(shí)間

---------?MixDigest和Nonce是跟挖礦過(guò)程相關(guān)的巾钉。

nonce:是挖礦時(shí)猜的那個(gè)隨機(jī)數(shù)(類(lèi)似于比特幣的挖礦),以太坊中的挖礦也是要猜很多個(gè)隨機(jī)數(shù)秘案,寫(xiě)在塊頭里的隨機(jī)數(shù)是最后找到的砰苍,符合難度要求的

MixDigest:混合摘要,從nonce這個(gè)隨機(jī)數(shù)經(jīng)過(guò)一些計(jì)算阱高,算出一個(gè)哈希值赚导。


2、區(qū)塊的結(jié)構(gòu)

對(duì)這節(jié)課來(lái)說(shuō)比較相關(guān)的就是前面三個(gè)域:header赤惊,uncles吼旧,transactions

header:就是指向block header的指針

uncles:是指向叔父區(qū)塊的header的指針。而且是個(gè)數(shù)組未舟,因?yàn)橐粋€(gè)區(qū)塊可以有多個(gè)叔父區(qū)塊

transactions:就是這個(gè)區(qū)塊中交易的列表


這個(gè)區(qū)塊在網(wǎng)上發(fā)布的時(shí)候圈暗,發(fā)布的就是這些信息,其實(shí)就是剛剛上面的前三項(xiàng)真正發(fā)布出去裕膀。


狀態(tài)樹(shù)中的value的存儲(chǔ):RLP

狀態(tài)樹(shù)中保存的是(key员串,value)

key就是地址。講到現(xiàn)在昼扛,主要講的是鍵值寸齐,這個(gè)地址的管理方式。

那么這個(gè)value呢,這個(gè)賬戶(hù)的狀態(tài)呢访忿,是怎么存儲(chǔ)在狀態(tài)樹(shù)當(dāng)中的瞧栗?

實(shí)際上是要經(jīng)過(guò)一個(gè)序列化(RLP)的過(guò)程,然后再存儲(chǔ)海铆。

RLP:Recursive Length Prefix迹恐,遞歸長(zhǎng)度前綴,是一種序列化方法卧斟。特點(diǎn)是簡(jiǎn)單殴边,極簡(jiǎn)主義,越簡(jiǎn)單越好珍语。

Protocal buffer:簡(jiǎn)稱(chēng)Protobuf锤岸,是個(gè)很有名的做序列化的庫(kù)。

跟這些庫(kù)相比板乙,RLP的理念就是越簡(jiǎn)單越好是偷。

它只支持一種類(lèi)型:nested array bytes,嵌套數(shù)組字節(jié)募逞。一個(gè)一個(gè)字節(jié)組成的數(shù)組蛋铆,可以嵌套。

以太坊里的所有的其他類(lèi)型放接,比如整數(shù)刺啦,或者比較復(fù)雜的哈希表,最后都要變成nested array bytes纠脾。

所以實(shí)現(xiàn)RLP要比實(shí)現(xiàn)Protocal buffer簡(jiǎn)單很多玛瘸,因?yàn)殡y的東西,他都不做苟蹈,都推給應(yīng)用層了糊渊。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市慧脱,隨后出現(xiàn)的幾起案子再来,更是在濱河造成了極大的恐慌,老刑警劉巖磷瘤,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芒篷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡采缚,警方通過(guò)查閱死者的電腦和手機(jī)针炉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扳抽,“玉大人篡帕,你說(shuō)我怎么就攤上這事殖侵。” “怎么了镰烧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵拢军,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我怔鳖,道長(zhǎng)茉唉,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任结执,我火速辦了婚禮度陆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘献幔。我一直安慰自己懂傀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布蜡感。 她就那樣靜靜地躺著蹬蚁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪郑兴。 梳的紋絲不亂的頭發(fā)上缚忧,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音杈笔,去河邊找鬼。 笑死糕非,一個(gè)胖子當(dāng)著我的面吹牛蒙具,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播朽肥,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼禁筏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了衡招?” 一聲冷哼從身側(cè)響起篱昔,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎始腾,沒(méi)想到半個(gè)月后州刽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浪箭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年穗椅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奶栖。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡匹表,死狀恐怖门坷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情袍镀,我是刑警寧澤默蚌,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站苇羡,受9級(jí)特大地震影響绸吸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宣虾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一惯裕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绣硝,春花似錦蜻势、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至甫菠,卻和暖如春挠铲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寂诱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工拂苹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人痰洒。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓瓢棒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親丘喻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子脯宿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容