此文我們會(huì)深入討論以太坊數(shù)據(jù)存儲(chǔ)層。我們會(huì)介紹區(qū)塊鏈“狀態(tài)”的概念。同時(shí)也會(huì)討論P(yáng)atricia前綴樹結(jié)構(gòu)背后的理論,使用谷歌的leveldb數(shù)據(jù)庫(kù)演示以太坊前綴樹的具體實(shí)現(xiàn)。
在存儲(chǔ)層中,我們存儲(chǔ)的是什么?
首先我們需要理解為了讓區(qū)塊鏈系統(tǒng)運(yùn)行,我們需要存儲(chǔ)的東西。讓我們簡(jiǎn)單地看下關(guān)于Alice給Bob轉(zhuǎn)賬10美金的例子。
我們可以看出,通過執(zhí)行轉(zhuǎn)賬可以改變其中的狀態(tài)。
我們必須要追蹤余額以及不同人(狀態(tài))的其他細(xì)節(jié),還有在區(qū)塊鏈之間發(fā)生的細(xì)節(jié)(轉(zhuǎn)賬)。不同的平臺(tái)會(huì)有不同地處理方法。我們可以看出,比特幣和以太坊是如何處理的。
比特幣
比特幣的狀態(tài)是通過UTXO來實(shí)現(xiàn)的。比特幣價(jià)值轉(zhuǎn)移是通過轉(zhuǎn)賬實(shí)現(xiàn)的。更特別地是,比特幣用戶可以通過創(chuàng)建轉(zhuǎn)賬花費(fèi)1個(gè)或多個(gè)UTXO,并且將他們的UTXO作為轉(zhuǎn)賬輸入。
UTXO模型讓比特幣和以太坊不同。我們可以看這些例子來理解其中的區(qū)別。
首先,比特幣UTXO不能部分花費(fèi)。如果比特幣用戶花費(fèi)0.5個(gè)比特幣(使用他們僅有的UTXO,價(jià)值1比特幣),他們需要特意地發(fā)回0.5個(gè)比特幣。如果他們不發(fā)送這部分,那么這個(gè)0.5比特幣就會(huì)丟失,并且給到挖出轉(zhuǎn)賬的礦工。
其次,在最基本的層面,比特幣沒有包含用戶賬戶余額。通過比特幣,用戶可以簡(jiǎn)單地持有私鑰,在任何時(shí)間點(diǎn)都可以進(jìn)行一個(gè)或者多個(gè)UTXO。數(shù)字錢包看起來像是讓比特幣區(qū)塊鏈能夠自動(dòng)地存儲(chǔ)和管理用戶賬戶余額,其實(shí)不是這樣。
比特幣的UTXO系統(tǒng)工作的很好,這是由于數(shù)字錢包能夠完成大多數(shù)轉(zhuǎn)賬任務(wù)。其中包括,但是不限于:
a) 處理UTXO
b) 存儲(chǔ)秘鑰
c) 設(shè)置轉(zhuǎn)賬費(fèi)用
d) 提供返回地址
e)描述UTXO狀態(tài)(展示可行性,待轉(zhuǎn)賬以及全部的余額)
UTXO模型中的轉(zhuǎn)賬可以類比為紙幣轉(zhuǎn)賬。每個(gè)賬戶都會(huì)追蹤錢包添加的賬單(UTXO)。當(dāng)我們想要花錢的時(shí)候,我們會(huì)使用一個(gè)或者多個(gè)賬單(現(xiàn)在的UTXO),這已經(jīng)足夠來承擔(dān)花銷,或許還會(huì)得到一些找零。(新的UTXO)。每個(gè)賬單只能花費(fèi)一次,一旦消費(fèi),UTXO就會(huì)從資金池移走。
總結(jié)下來,我們知道:
? 比特幣區(qū)塊鏈不會(huì)持有賬戶余額
? 的秘鑰
? 如果包含在轉(zhuǎn)賬中,完整的UTXO會(huì)被消費(fèi)(有時(shí)候,部分會(huì)得到新的UTXO作為找零)
以太坊
和以上的信息相反,以太坊的狀態(tài)能夠管理賬戶余額,以及更多信息。以太坊的狀態(tài)并不是個(gè)抽象的概念。它是以太坊底層協(xié)議的部分。根據(jù)黃皮書中的描述,以太坊是一個(gè)基于轉(zhuǎn)賬的狀態(tài)機(jī)器;基于狀態(tài)機(jī)器的轉(zhuǎn)賬技術(shù)能夠被創(chuàng)建。
我們從頭開始來講述。和其他區(qū)塊鏈一樣,以太坊區(qū)塊鏈也是從創(chuàng)世區(qū)塊開始的。從那時(shí)候起,例如轉(zhuǎn)賬,合約和挖礦之類的事情,都會(huì)陸續(xù)改變以太坊區(qū)塊鏈的狀態(tài)。在以太坊中,舉例來說就是賬戶余額(存儲(chǔ)在狀態(tài)樹中),這會(huì)隨著轉(zhuǎn)賬而改變,同時(shí)和賬戶相關(guān)連。
重要地是,例如賬戶余額之類的數(shù)據(jù)并不是直接存儲(chǔ)在以太坊區(qū)塊鏈的區(qū)塊中。只有根節(jié)點(diǎn)哈希的轉(zhuǎn)賬,狀態(tài)數(shù)據(jù)和回執(zhí)數(shù)據(jù)是直接存儲(chǔ)在區(qū)塊鏈上的。可以根據(jù)下圖看出。
也許你也注意到了,從上面的圖表中,存儲(chǔ)樹的根節(jié)點(diǎn)哈希(所有的智能合約數(shù)據(jù)存儲(chǔ)在其中)其實(shí)都是指向狀態(tài)樹的,從而指向區(qū)塊鏈。接下來,我們會(huì)討論更多細(xì)節(jié)。
以太坊中有兩種不同的數(shù)據(jù)類型:永久數(shù)據(jù)和暫時(shí)數(shù)據(jù)。永久數(shù)據(jù)的例子就是轉(zhuǎn)賬。一旦轉(zhuǎn)賬確認(rèn),就會(huì)在區(qū)塊鏈中記錄;然后就再也不可以更改。暫時(shí)數(shù)據(jù)的例子就是特定以太坊賬戶地址的余額。賬戶的余額就會(huì)存儲(chǔ)在狀態(tài)樹中,并且當(dāng)有特定賬戶轉(zhuǎn)賬的時(shí)候,就會(huì)改變。永久數(shù)據(jù)是有意義的,就好像挖礦轉(zhuǎn)賬,暫時(shí)數(shù)據(jù),就例如賬戶余額,應(yīng)該被分開存儲(chǔ)。以太坊會(huì)使用數(shù)據(jù)樹結(jié)構(gòu)來管理數(shù)據(jù)。
以太坊的數(shù)據(jù)記錄就好像在銀行。類似使用ATM機(jī)器和存儲(chǔ)卡。銀行會(huì)追蹤每個(gè)借記卡來確保在在完成轉(zhuǎn)賬之前,有足夠的余額。
UTXO和賬戶方案之間的對(duì)比
UTXO模型的好處:
? 擴(kuò)容性 – 因?yàn)榭梢酝瑫r(shí)處理多個(gè)UTXO,所以能夠完成同步轉(zhuǎn)賬并且鼓勵(lì)擴(kuò)容創(chuàng)新。
? 隱私 – 盡管比特幣并是不完全的匿名系統(tǒng),但是UTXO可以提供更高層次的隱私性,只要用戶使用為每個(gè)轉(zhuǎn)賬提供新的地址。如果有需要提高隱私性,更多復(fù)雜的結(jié)構(gòu),例如環(huán)形結(jié)構(gòu),也可以考慮使用。
賬戶/余額模式的好處:
? 簡(jiǎn)單化- 以太坊使用的模型,可以幫助開發(fā)者來進(jìn)行復(fù)雜的智能合約,特別是需要狀態(tài)信息或者包含多方的。
舉例來說,追蹤狀態(tài)的智能合約,并且基于它處理不同的任務(wù)。UTXO的無狀態(tài)模型會(huì)讓轉(zhuǎn)賬包含狀態(tài)信息,而且這也不必要地符合合約的設(shè)計(jì)。
? 效率- 除了簡(jiǎn)單化,賬戶/余額模型更加有效,因?yàn)槊總€(gè)轉(zhuǎn)賬都只需要來驗(yàn)證發(fā)出金額的賬戶是否有足夠的余額來支付轉(zhuǎn)賬。
賬戶/余額模型的缺陷是雙花攻擊。可以增加遞增的隨機(jī)數(shù)來抵消這種類型的攻擊。在以太坊中,每個(gè)賬戶都有空開可見的隨機(jī)數(shù),每次進(jìn)行轉(zhuǎn)賬的時(shí)候,隨機(jī)數(shù)就會(huì)增加。這可以幫助防止同樣的轉(zhuǎn)賬會(huì)進(jìn)行兩次。(注意,這個(gè)隨機(jī)數(shù)并不是工作量證明中的隨機(jī)數(shù),這是個(gè)隨機(jī)數(shù)字)
和大多數(shù)計(jì)算機(jī)架構(gòu)相同,這兩個(gè)模型都有自己的好處和壞處。有些區(qū)塊鏈,例如超級(jí)賬本,也應(yīng)用了UTXO,因?yàn)樗麄儚谋忍貛艆^(qū)塊鏈中獲得創(chuàng)新。接下來,我們來看看更多的基于這兩個(gè)模型的技術(shù)。
以太坊中的數(shù)據(jù)樹結(jié)構(gòu)是什么?
我們來深入看看,狀態(tài),存儲(chǔ)和轉(zhuǎn)賬的樹結(jié)構(gòu)是怎樣的。
狀態(tài)前綴樹- 是唯一和獨(dú)特的。
在以太坊中,只有唯一的網(wǎng)絡(luò)狀態(tài)前綴樹。
這個(gè)網(wǎng)絡(luò)狀態(tài)前綴樹會(huì)實(shí)時(shí)更新。
網(wǎng)絡(luò)狀態(tài)前綴樹包含秘鑰和每個(gè)賬戶的價(jià)值對(duì),這些是在以太坊網(wǎng)絡(luò)上。
秘鑰是單個(gè)160字節(jié)的認(rèn)證器(以太坊賬戶的地址)。
網(wǎng)絡(luò)狀態(tài)前綴樹的“數(shù)值”是通過對(duì)以太坊賬戶以下賬戶細(xì)節(jié)的編譯得出的:
-隨機(jī)數(shù)
-余額
-storageRoot
-codeHash
狀態(tài)前綴樹的根節(jié)點(diǎn)(某個(gè)時(shí)間點(diǎn),整個(gè)網(wǎng)絡(luò)狀態(tài)前綴樹的哈希)是用來保證狀態(tài)前綴樹的安全和唯一;網(wǎng)絡(luò)狀態(tài)前綴樹根節(jié)點(diǎn)是基于整個(gè)內(nèi)部網(wǎng)絡(luò)狀態(tài)前綴樹數(shù)據(jù)進(jìn)行加密。
存儲(chǔ)前綴樹,智能合約數(shù)據(jù)存儲(chǔ)的地方
存儲(chǔ)前綴樹是智能合約數(shù)據(jù)存儲(chǔ)的地方。每個(gè)以太坊賬戶都有自己的存儲(chǔ)前綴樹。存儲(chǔ)前綴樹根節(jié)點(diǎn)是256字節(jié)的哈希值,作為storageRoot的數(shù)值存儲(chǔ)在網(wǎng)絡(luò)狀態(tài)前綴樹。
轉(zhuǎn)賬前綴樹- 每個(gè)區(qū)塊都有一個(gè)
每個(gè)以太坊區(qū)塊都有自己獨(dú)立的轉(zhuǎn)賬前綴樹。一個(gè)區(qū)塊會(huì)包含很多轉(zhuǎn)賬。區(qū)塊中的轉(zhuǎn)賬順序當(dāng)然是由礦工來決定的。對(duì)于轉(zhuǎn)賬前綴樹中的特殊轉(zhuǎn)賬路徑,是通過這個(gè)轉(zhuǎn)賬在區(qū)塊中的位置因子。挖礦區(qū)塊不會(huì)更新;轉(zhuǎn)賬在區(qū)塊中的位置不會(huì)改變。這意味著一旦你在區(qū)塊轉(zhuǎn)賬前置樹中定位了轉(zhuǎn)賬,你可以返回到同樣的路徑來獲得同樣的結(jié)果。
分析以太坊數(shù)據(jù)庫(kù)
在以太坊區(qū)塊鏈中,有很多的MPT(Merkle Patricia Tries)(代表每個(gè)區(qū)塊):
? 狀態(tài)前綴樹
? 存儲(chǔ)前綴樹
? 轉(zhuǎn)賬前綴樹
? 回執(zhí)前綴樹
為了得到某個(gè)特定區(qū)塊中的MPT,我們需要獲得它的跟哈希,作為參考。以下的命令可以讓我們獲得狀態(tài),轉(zhuǎn)賬和創(chuàng)世區(qū)塊中回執(zhí)的根哈希。
注意:如果你想得到最新區(qū)塊(而不是創(chuàng)世區(qū)塊)的根哈希,請(qǐng)使用以下命令。
安裝npm,節(jié)點(diǎn),level和ethereumjs
我們會(huì)使用nodejs,level和ethereumjs 的結(jié)合來檢測(cè)leveldb數(shù)據(jù)庫(kù)。以下的命令可以幫助我們準(zhǔn)備測(cè)試環(huán)境。
cd ~
sudo apt-get update
sudo apt-get upgrade
curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash - sudo apt-get install -y nodejs
sudo apt-get install nodejs
npm -v
nodejs -v
npm install levelup leveldown rlp merkle-patricia-tree --save
git clone https://github.com/ethereumjs/ethereumjs-vm.git
cd ethereumjs-vm
npm install ethereumjs-account ethereumjs-util --save
從這時(shí)候開始,運(yùn)行以下代碼會(huì)得到以太坊賬戶秘鑰(會(huì)存儲(chǔ)在以太坊網(wǎng)絡(luò)的狀態(tài)根部)。代碼和以太坊leveldb數(shù)據(jù)庫(kù)連接,進(jìn)入以太坊的狀態(tài)(從區(qū)塊鏈的區(qū)塊中使用stateRoot數(shù)值),并且然后可以使用秘鑰進(jìn)入到以太坊網(wǎng)絡(luò)中的所有賬戶。
//Just importing the requirements
var Trie = require(‘merkle-patricia-tree/secure’);
var levelup = require(‘levelup’);
var leveldown = require(‘leveldown’);
var RLP = require(‘rlp’);
var assert = require(‘a(chǎn)ssert’);
//Connecting to the leveldb database
var db = levelup(leveldown(‘/home/timothymccallum/gethDataDir/geth/chaindata’));
//Adding the “stateRoot” value from the block so that we can inspect the state root at that block height.
var root = ‘0x8c77785e3e9171715dd34117b047dffe44575c32ede59bde39fbf5dc074f2976’;
//Creating a trie object of the merkle-patricia-tree library
var trie = new Trie(db, root);
//Creating a nodejs stream object so that we can access the data
var stream = trie.createReadStream()
//Turning on the stream (because the node js stream is set to pause by default)
stream.on(‘data’, function (data){
//printing out the keys of the “state trie”
console.log(data.key);
});
有趣地是,一旦轉(zhuǎn)賬發(fā)生了,以太坊中的賬戶只是添加到狀態(tài)樹中(和那個(gè)特定賬戶相關(guān)的)。例如,使用“geth account new”創(chuàng)建新的賬戶不會(huì)包含在狀態(tài)樹中包含那個(gè)賬戶;甚至在很多區(qū)塊被挖出后。但是,如果成功的轉(zhuǎn)賬(花費(fèi)燃料費(fèi)并且已經(jīng)包含在挖礦區(qū)塊)是記錄在賬戶中,然后只有它會(huì)出現(xiàn)在狀態(tài)樹中。這是很聰明的邏輯,因?yàn)闀?huì)保護(hù)欺詐者無法連續(xù)創(chuàng)建新的賬戶以及使得狀態(tài)樹堵塞。
對(duì)數(shù)據(jù)解碼
你已經(jīng)注意到,查詢leveldb可以回復(fù)解碼的結(jié)果。這是由于,以太坊使用了自己特定的“修改版的MPT(Merkle Patricia Trie)”,用來和leveldb進(jìn)行交互。以太坊Wiki提供了設(shè)計(jì)和部署以太坊MPT(Merkle Patricia Trie)和RLP(Recursive Length Prefix)解碼的信息。簡(jiǎn)單地說,以太坊已經(jīng)在前綴樹數(shù)據(jù)結(jié)構(gòu)擴(kuò)展。例如,修改版的MPT(Merkle Patricia Trie)包含一種通過“extension”節(jié)點(diǎn),來創(chuàng)建快捷方式的方法。
在以太坊中,單個(gè)的修改版的MPT(Merkle Patricia Trie)節(jié)點(diǎn)是:
? 空的字節(jié)(對(duì)應(yīng)NULL)
? 包含17個(gè)對(duì)象的數(shù)組(對(duì)應(yīng)分支)
? 包含2個(gè)對(duì)象的數(shù)組(對(duì)應(yīng)樹葉)
? 包含2個(gè)對(duì)象的數(shù)組(對(duì)應(yīng)擴(kuò)展)
以太坊前綴樹是通過固定的規(guī)則來設(shè)計(jì)和創(chuàng)建的,最好的檢測(cè)方法是使用電腦代碼。接下來的例子使用了ethereumjs。Ethereumjs很容易安裝和使用;它是完美地可以快速對(duì)接到以太坊leveldb數(shù)據(jù)庫(kù)。
下面的代碼(當(dāng)提供一個(gè)特定的區(qū)塊stateRoot以及以太坊賬戶地址)會(huì)以可讀的形式返回賬戶的正確余額。
//Mozilla Public License 2.0
//As per https://github.com/ethereumjs/ethereumjs-vm/blob/master/LICENSE
//Requires the following packages to run as nodejs file https://gist.github.com/tpmccallum/0e58fc4ba9061a2e634b7a877e60143a
//Getting the requirements
var Trie = require(‘merkle-patricia-tree/secure’);
var levelup = require(‘levelup’);
var leveldown = require(‘leveldown’);
var utils = require(‘ethereumjs-util’);
var BN = utils.BN;
var Account = require(‘ethereumjs-account’);
//Connecting to the leveldb database
var db = levelup(leveldown(‘/home/timothymccallum/gethDataDir/geth/chaindata’));
//Adding the “stateRoot” value from the block so that we can inspect the state root at that block height.
var root = ‘0x9369577baeb7c4e971ebe76f5d5daddba44c2aa42193248245cf686d20a73028’;
//Creating a trie object of the merkle-patricia-tree library
var trie = new Trie(db, root);
var address = ‘0xccc6b46fa5606826ce8c18fece6f519064e6130b’;
trie.get(address, function (err, raw) {
if (err) return cb(err)
//Using ethereumjs-account to create an instance of an account
var account = new Account(raw)
console.log(‘Account Address: ’ + address);
//Using ethereumjs-util to decode and present the account balance
console.log(‘Balance: ’ + (new BN(account.balance)).toString());
})
結(jié)論
我們已經(jīng)表現(xiàn)出以太坊有能力來管理狀態(tài)。這種超前的設(shè)計(jì)有很多好處。
可移動(dòng)性
假設(shè)移動(dòng)設(shè)備和物聯(lián)網(wǎng)設(shè)備是很普遍的,未來電商就取決于安全,穩(wěn)定和快速的移動(dòng)應(yīng)用。
我們認(rèn)知到了可移動(dòng)性的優(yōu)勢(shì),我們也知道區(qū)塊鏈大小的逐漸增加是難以置信的。將整個(gè)區(qū)塊鏈存儲(chǔ)在移動(dòng)設(shè)備是不可能的。
快速,并且不會(huì)損失安全性
以太坊狀態(tài)的設(shè)計(jì)以及對(duì)于修改版的MPT(Merkle Patricia Trie)的使用,提供了很多機(jī)會(huì)。以太坊前綴樹上的每個(gè)功能都使用了加密哈希。而且,前綴樹根據(jù)節(jié)點(diǎn)的特殊加密哈希可以用來證明前綴樹沒有被欺詐。
例如,任何對(duì)于前綴樹的修改,都會(huì)完全改變根部哈希。這個(gè)加密功能會(huì)為輕客戶端提供一個(gè)機(jī)會(huì)(那些沒有存儲(chǔ)整個(gè)區(qū)塊鏈的設(shè)備),從而可以快速地訪問區(qū)塊鏈。也就是說,賬戶“0x … 4857”是否有足夠的資金來完成對(duì)于區(qū)塊高度“5044866”的轉(zhuǎn)賬?
速度限制
以太坊描述了個(gè)很有趣的問題,就是存儲(chǔ)賬戶的概念。想象這種場(chǎng)景,兩個(gè)用戶都可以每天從賬戶中拿出全部余額的1%。這個(gè)觀點(diǎn)只在未來規(guī)劃中提到,但是它卻獲得了很多興趣,因?yàn)槔碚撋蟻碚f,它可以作為以太坊基礎(chǔ)協(xié)議層的一部分(和必須要作為第二層和第三方錢包相反)。也許你想起了我們之前討論的比特幣UTXO。UTXO對(duì)于區(qū)塊鏈數(shù)據(jù)是盲目的,比特幣區(qū)塊鏈沒有存儲(chǔ)用戶的賬戶余額。因此,比特幣的底層協(xié)議層基本上不可能完成任何類型的每日速度限制。
消費(fèi)者的信心
我們看到了關(guān)于輕客戶端的很多開發(fā),更為特別地是,安全、穩(wěn)定、快速的移動(dòng)應(yīng)用,可以和區(qū)塊鏈技術(shù)交互。
電子商務(wù)的區(qū)塊鏈成功部署,一定會(huì)支持速度,安全和可用性。這能夠提高消費(fèi)者的信心,同時(shí)也通過聰明的設(shè)計(jì),提供更高的可用性,安全性和性能,進(jìn)而提高了主流的接受能力。
評(píng)論
查看更多