這篇文章的內容是《以太坊技術詳解與實戰》第二章抽取出來總結而成的,大致上能一窺以太坊的全貌。web
以太坊的總體架構爲三層:底層服務、核心層、頂層應用,如圖:算法
所謂的區塊,其實能夠定義爲記錄一段時間內發生的交易和狀態結果的數據結構,是對當前帳本狀態的一次共識。數據庫
區塊主要由區塊頭、交易列表和叔區塊頭三部分組成:數組
區塊頭包含:父塊的散列值( Prev Hash )、叔區塊的散列值( Uncles Hash )、狀態樹根散列值( stateRoot ) 、交易樹根散列值( Transaction Root ) 、收據樹根散列值( Receipt Root )、時間戳( Times tamp )、隨機數( Nonce )等緩存
以太坊區塊鏈上區塊數據結構相對比特幣的一個重大改變就是保存了三棵Merkle樹根,分別是狀態樹、交易樹和收據樹。安全
交易列表是由礦工從交易池中選擇收入區塊中的一系列交易。網絡
不在主鏈上的且被主鏈上的區塊經過Uncles 字段收留進區塊鏈的孤塊叫作「叔區塊」 。數據結構
帳戶以地址爲索引,地址由公鑰衍生而來,取公鑰的最後20 字節架構
在以太坊系統中存在兩種類型的帳戶,分別是外部帳戶和合約帳戶。外部帳戶存儲以太幣餘額狀態,而合約帳戶除了餘額還有智能合約及其變量的狀態。分佈式
外部帳戶( Externally Owned Account, EOA )由私鑰來控制,是由用戶實際控制的帳戶。存儲以太幣餘額狀態
合約帳戶是一個包含合約代碼的帳戶。合約帳戶不是由私鑰文件直接控制,而是由合約代碼控制。合約帳戶的地址是由合約建立時合約建立者的地址,以及該地址發出的交易共同計算得出的。
與外部帳戶相比,合約帳戶除了餘額還有智能合約及其變量的狀態
以太坊中每一個外部帳戶都由一對密匙定義,即一個私鑰和一個公鑰。
目前常見的私鑰有三種形態:
1 ) Private key
Private key 就是一份隨機生成的256 位二進制數字。用戶甚至能夠用紙筆來隨機地生成一個私鑰,即隨機寫下一串256 位的僅包含「0」或「 1」的字符串。該256 位二進制數字就是私鑰最初始的狀態。
2 )Keystore & Password
而在以太坊官方錢包中,私鑰和公鑰將會以加密的方式保存一份JSON 文件,存儲在key store 子目錄下。這份JSON 文件就是Keys tore ,因此用戶須要同時備份Keys tore和對應的Password (建立錢包時設置的密碼) 。
3 )Memonic code
Memonic code 是由BIP 39 方案提出的,目的是隨機生成12 ~ 24 個比較容易記住的單詞,該單詞序列經過PBKDF2 與HMAC-SHA512 函數建立出隨機種子,該種子經過BIP-0032 提案的方式生成肯定性錢包
區塊、交易等數據最終都是存儲在Level DB數據庫中。Level DB數據庫是一個鍵值對( key-value )數據庫, key通常與散列相關,value則是存儲內容的RLP編碼。
以太坊使用了MPT樹(Merkle Patricia Trie),做爲數據組織形式,用來組織管理用戶的帳戶狀態、交易信息等重要數據。MPT是一種加密認證的數據結構,它融合了Merkle樹和Trie樹(前綴樹)兩種數據類型的優勢。
Merkle樹是一種樹形數據結構,能夠是二叉樹,也能夠是多叉樹。它由一組葉節點、一組中間節點和一個根節點構成。最下面的葉節點包含基礎數據,每一箇中間節點是它的子節點的散列,根節點是它的子節點的散列,表明了Merkle樹的根部。
建立Merkle樹的目的是容許區塊的數據能夠零散地傳送;節點能夠從一個節點下載區塊頭,從另外的源下載與其相關的樹的其餘部分,而依然可以確認全部的數據都是正確的。
Merkle樹能夠用來存儲全部鍵值對
Merkle樹具備下列特性:
1.每一個數據集對應一個惟一合法的根散列值。
2.很容易更新、添加或者刪除樹節點,以及生成新的根散列值。
3.不改變根散列值的話就沒有辦法修改樹的任何部分,因此若是根散列值被包括在簽名的文檔或有效區塊中,就能夠保證這棵樹的正確性。
4.任何人能夠只提供一個到特定節點的分支,並經過密碼學方法證實擁有對應內容的節點確實在樹裏。
key表明的是從樹根到對應value的一條路徑。即從根節點開始, key 中的每一個字符(從前到後)都表明着從根節點出發尋找相應value 所要通過的子節點。value 存儲在葉節點中,是每條路徑的最終節點。
結合merkle和Trie,並作了如下改進:
1.爲了保證樹的加密安全,每一個節點經過它的散列值被引用
2.對於存儲在Leve IDB 數據庫中的非葉節點,key表明着節點的RLP編碼的SHA3散列值, value是節點的RLP編碼。
3.引入了不少節點類型來提升效率:
3.1空節點:簡單的表示空,在代碼中就是一個空串
3.2葉節點:鍵值對的一個列表,其中key是一種特殊的十六進制編碼, value是RLP編碼。
3.3擴展節點:鍵值對的列表,可是這裏的value 是其餘節點的散列值,經過這個散列值能夠連接到其餘節點。
3.4分支節點:一個長度爲17 的列表。MPT 中的key 被編碼成一種特殊的十六進制的表示,再加上最後的value ,前16 個元素對應key 中的16 個可能的十六進制字符,若是有一個鍵值對在這個分支節點終止,則最後一個元素表明一個值,即分支節點既能夠是搜索路徑的終止,也能夠是路徑的中間節點。
4.用於對key 進行編碼的特殊十六進制前綴編碼( HP )
如圖
狀態樹包含一個鍵值映射,其中鍵是帳戶地址,值是帳戶內容,主要是{ nonce, balance,codeHash, storageRoot } 。nonce 是帳戶交易的序數, balance 是帳戶餘額, codeHash 是代碼的散列值, storageRoot 是另外一棵樹的根節點。狀態樹表明訪問區塊後的整個狀態。
以太坊是一個以帳戶爲基礎的區塊鏈應用平臺,帳戶的狀態不是直接存儲在每一個區塊中,全部的帳戶狀態都是以「狀態數據」的形式存儲在以太坊的節點中。
每一個區塊都有一棵獨立的交易樹。
區塊中交易的順序主要由「礦工」決定,在這個塊被挖出前這些數據都是未知的。不過「礦工」 通常會根據交易的GasPrice 和nonce 對交易進行排序。首先會將交易列表中的交易劃分到各個發送帳戶,每一個帳戶的交易根據這些交易的nonce 來排序。每一個帳戶的交易排序完成後,再經過比較每一個帳戶的第一條交易,選出最高價格的交易,這些是經過一個堆(heap)來實現的
在交易樹包含的鍵值對中,其中每一個鍵是交易的編號,值是交易內容
每一個區塊都有本身的收據樹,收據樹不須要更新,收據樹表明每筆交易相應的收據。
交易的收據是一個RLP 編碼的數據結構:[medstate, Gas_ used, logbloom,logs ] 。其中, medstate 是交易處理後樹根的狀態; Gas_used 是交易處理後Gas 的使用量;logs 是表格[ address, [topicl, topic2 ,…], data ]元素的列表,表格由交易執行期間調用的操做碼LOGO …LOG4 生成(包含主調用和子調用), address 是生成日誌的合約地址, topicn是最多4 個32 字節的值, data 是任意字節大小的數組; logbloom 是交易中全部logs 的address 和topic 組成的布隆過濾器.
LeveI DB 是Goog le 實現的一個很是高效的鍵值對數據庫,其中鍵值都是二進制的,目前可以支持十億級別的數據量,在這個數據量下還有着很是高的性能。
以太坊中共有三個Leve IDB 數據庫,分別是BlockDB 、StateDB 和ExtrasDB。
BlockDB保存了塊的主體內容,包括塊頭和交易;StateDB保存了帳戶的狀態數據;ExtrasDB保存了收據信息和其餘輔助信息。
共識機制是區塊鏈事務達成分佈式共識的算法。因爲點對點網絡下存在着或高或低的網絡延遲,因此各個節點接收到的事務的前後順序可能不同,所以區塊鏈系統須要設計一種機制讓節點對在差很少時間內發生的事務的前後順序實現共識,這就是共識機制。
PoW 即經過工做結果來證實你完成了相應的工做
哈希函數的特徵:
1.免碰撞,即不存在輸入值不一樣,通過散列變換,而散列值相同的狀況。
2.隱匿性,即給定一個散列值,想要反向逆推出輸入值,在計算上是不可行的。
3.不存在比窮舉更好的方法,以使得散列值落在特定的範圍。
POW算法原理:節點經過不斷地更換隨機數來探尋合適的哈希值,當節點最早計算出合適的哈希值,它所打包的塊若是經過其餘共識節點的驗證,則會被加入到區塊鏈中。
爲了解決挖礦中心化問題,專門設計了一個能抵制ASIC、輕客戶端可快速驗證的PoW 算法
算法流程:
1.對於每個區塊,都能經過掃描區塊頭的方式計算出一個種子( seed ),該種子只與當前區塊有關。
2.使用種子能產生一個16MB 的僞隨機緩存,輕客戶端會存儲緩存。
3.基於緩存再生成一個1GB 的數據集,稱其爲DAG 。數據集中的每個元素都只依賴於緩存中的某幾個元素,也就是說,只要有緩存,就能夠快速地計算出DAG 中指定位置的元素。挖礦者存儲數據集,數據集隨時間線性增加。
4.挖礦能夠歸納爲「礦工」從DAG 中隨機選擇元素並對其進行散列的過程, DAG 也能夠理解爲一個完整的搜索空間,挖礦的過程就是從DAG 中隨機選擇元素(相似比特幣挖礦中試探合適nonce 的過程)進行散列運算。
5.驗證者只須要花費少許的內存存儲緩存就能夠了,由於驗證者可以基於緩存計算獲得DAG 中本身須要的指定位置的元素,而後驗證這些指定元素的散列是否是小於某個散列值,也就是驗證「礦工」的工做是否符合要求。
Ethash 算法的特色是挖礦的效率基本與CPU 無關,而與內存大小、帶寬正相關,目的是去除專用硬件的優點,抵抗ASIC 。
PoS 即基於網絡參與者目前所持有的數字貨幣的數量和時間進行利益分配,是一種對貨幣全部權的證實
共識算法類型:
基於鏈的PoS 和BFT (Byzantine Fault Tolerant ,拜占庭容錯)風格的PoS 。
在基於鏈的PoS 中,該算法在每一個時隙內僞隨機地從驗證者集合中選擇一個驗證者(好比,設置每l0s 一個週期,每一個週期都是一個時隙),給予驗證者建立新區塊的權利,可是驗證者要確保該塊指向最多的塊(指向的上一個塊一般是最長鏈的最後一個塊) 。所以,隨着時間的推移,大多數的塊都收斂到一條鏈上。
在BFT 風格的PoS 中,分配給驗證者相對的權利,讓他們有權提出塊而且給被提出的塊投票,從而決定哪一個塊是新塊,並在每一輪選出一個新塊加入區塊鏈。在每一輪中,每個驗證者都爲某一特定的塊進行「投票」,最後全部在線和誠實的驗證者都將「商量」被給定的塊是否能夠添加到區塊鏈中,而且意見不能改變。
以太坊的交易主要是指一條外部帳戶發送到區塊鏈上另外一帳戶的消息的簽名數據包,其主要包含發送者的簽名、接收者的地址以及發送者轉移給接收者的以太幣數量等內容。
交易是以太坊總體架構中的重要部分,它將以太坊的帳戶鏈接起來,起到價值的傳遞做用。
from :交易發送者的地址,必填;
to :交易接收者的地址,若是爲空則意味這是一個建立智能合約的交易;
value :發送者要轉移給接收者的以太幣數量;
data (也寫做input):存在的數據字段,若是存在,則是代表該交易是一個建立或者調用智能合約交易;
Gas Limit (也寫做Gas, StartGas ):表示這個交易容許消耗的最大Gas 數量;
GasPrice :表示發送者願意支付給礦工的Gas 價格;
nonce :用來區別同一用戶發出的不一樣交易的標記;
hash :由以上信息生成的散列值(哈希值),做爲交易的ID;
r 、s 、v :交易簽名的三個部分,由發送者的私鑰對交易hash 進行簽名生成。
爲了防止用戶在區塊鏈公有鏈中發送太多的無心義交易,浪費礦工的計算資源,要求交易的發送方爲每筆交易付出必定的代價,即是交易費用。
因爲比特幣中只存在轉帳交易,每筆交易所需的計算開銷大致一致,所以每筆交易的發送者會以比特幣的形式,付出相對固定的手續費。而以太坊中引入了智能合約,涉及智能合約建立和調用的交易所消耗的計算差異巨大,所以引入了相對複雜的Gas 、Gas Price 對交易所需的手續費進行訂價。
1.Gas
Gas (汽油)是用來衡量一筆交易所消耗的計算資源的基本單位。當以太坊節點執行一筆交易所需的計算步驟越多、越複雜,那麼就會說這筆交易消耗的Gas 越多。
2.Gas Price
Gas Price ( Gas 價格)是一單位Gas 所需的手續費(以太幣,即Ether )。礦工會對接受到的交易按照Gas Price 或者按照Gas * Gas Price 從大到小進行排序,以便決定哪一個交易會先歸入到區塊中。當以太坊公有鏈上某個時段交易量激增的狀況下,爲了儘早讓礦工接受一筆交易,交易發送者能夠提升這筆交易的Gas Price ,以激勵礦工
3.Gas Limit
有兩種:
對於單個交易, Gas Limit (有時也會稱做StartGas )表示交易發送者願意爲這筆交易執行所支付的最大Gas 數量,須要發送者在發送交易時設置。能夠保護用戶免受錯誤代碼影響以至消耗過多的交易費。GasPrice * Gas Limit 表示用戶願意爲一筆交易支付的最高金額。
而對於區塊來講, Gas Limit 是單個區塊所容許包含的最大Gas 總量,由礦工決定它的大小。防止礦工的資源消耗過大,形成挖出的區塊沒法造成最長的交易鏈。不過礦工也不能任意地更改區塊的Gas Limit ,根據以太坊協議,當前區塊的Gas Limit 只能基於上一個區塊的Gas Limit 上下波動1/1024。
1.轉帳交易
web3.eth.sendTransaction({
from :"Oxb60e8dd6lc5d32be8058bb8eb970870f07233155",
to:"Oxd46e8dd67c5d32be8058bb8eb970870f07244567",
value: 10000000000000000
});
2.建立智能合約的交易
web3.eth.sendTransaction({
from:"Oxb60e8dd6lc5d32be8058bb8eb970870f07233155",
data :"contract binary code"
});
3.執行智能合約的交易
web3.eth.sendTransaction({
from:"Oxb60e8dd6lc5d32be8058bb8eb970870f07233155",
to:"Oxb4259e5d9bc67a0f2ce3ed372ffc5lbe46c33c4d",
data :"hash of the invoked method signature and encoded parameters"
});
RLP ( Recursive Length Prefix )是一種編碼算法,用於編碼任意的具備嵌套結構的二進制數據,是以太坊數據序列化的主要方法。
除了EVM和區塊同步協議外,其餘基本都覆蓋了,至於一些細節之處最好經過分析以太坊的源碼去釐清。