本文首發於深刻淺出區塊鏈社區
原文連接:以太坊創世區塊與鏈配置載入分析,原文已更新,請讀者前往原文閱讀。數據庫
創世區塊做爲第零個區塊,其餘區塊直接或間接引用到創世區塊。所以節點啓動之初必須載入正確的創世區塊信息,且不得任意修改。json
以太坊容許經過創世配置文件來初始化創世區塊,也可以使用選擇使用內置的多個網絡環境的創世配置。默認使用以太坊主網創世配置。bash
若是你須要搭建以太坊私有鏈,那麼瞭解創世配置是必須的,不然你大可不關心創世配置。下面是一份 JSON 格式的創世配置示例:網絡
{ "config": { "chainId": 1, "homesteadBlock": 1150000, "daoForkBlock": 1920000, "daoForkSupport": true, "eip150Block": 2463000, "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", "eip155Block": 2675000, "eip158Block": 2675000, "byzantiumBlock": 4370000, "constantinopleBlock": 7280000, "petersburgBlock": 7280000, "ethash": {} }, "nonce": "0x42", "timestamp": "0x0", "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", "gasLimit": "0x1388", "difficulty": "0x400000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "alloc": { "000d836201318ec6899a67540690382780743280": { "balance": "0xad78ebc5ac6200000" }, "001762430ea9c3a26e5749afdb70da5f78ddbb8c": { "balance": "0xad78ebc5ac6200000" } } }
根據配置用途可分爲三大類:學習
config
項是定義鏈配置,會影響共識協議,雖然鏈配置對創世影響不大,但新區塊的出塊規則均依賴鏈配置。nonce
:隨機數,對應創世區塊 Nonce
字段。timestamp
:UTC時間戳,對應創世區塊 Time
字段。extraData
:額外數據,對應創世區塊 Extra
字段。gasLimit
:必填,燃料上限,對應創世區塊 GasLimit
字段。difficulty
:必填,難度係數,對應創世區塊 Difficulty
字段。搭建私有鏈時,須要根據狀況選擇合適的難度值,以便調整出塊。minHash
:一個哈希值,對應創世區塊的MixDigest
字段。和 nonce 值一塊兒證實在區塊上已經進行了足夠的計算。coinbase
:一個地址,對應創世區塊的Coinbase
字段。alloc
項是創世中初始帳戶資產配置。在生成創世區塊時,將此數據集中的帳戶資產寫入區塊中,至關於預挖礦。這對開發測試和私有鏈很是好用,不須要挖礦就能夠直接爲任意多個帳戶分配資產。若是你計劃部署以太坊私有網絡或者一個獨立的測試環境,那麼須要自定義創世,並初始化它。爲了統一溝通,推薦先在用戶根目錄建立一個文件夾 deepeth
,以作爲《以太坊設計與實現》電子書學習工做目錄。區塊鏈
mkdir $HOME/deepeth && cd $HOME/deepeth
再準備兩個以太坊帳戶,以便在創世時存入資產。測試
geth --datadir $HOME/deepeth account new
由於是學習使用,推薦使用統一密碼 foobar
,執行兩次命令,建立好兩個帳戶。這裏使用 --datadir
參數指定以太坊運行時數據存放目錄,是讓你們將數據統一存放在一個本課程學習文件夾中。ui
再將下面配置內容保存到 $HOME/deepeth/genesis.json
文件,其中 alloc
項替換成剛剛建立的兩個以太坊帳戶地址。this
{ "config": { "chainId": 8888, "homesteadBlock": 0, "daoForkBlock": 0, "daoForkSupport": true, "eip150Block": 0, "eip155Block": 0, "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, "ethash": {} }, "nonce": "0x42", "timestamp": "0x0", "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", "gasLimit": "0x1388", "difficulty": "0x1", "alloc": { "093f59f1d91017d30d8c2caa78feb5beb0d2cfaf": { "balance": "0xffffffffffffffff" }, "ddf7202cbe0aaed1c2d5c4ef05e386501a054406": { "balance": "0xffffffffffffffff" } } }
而後,執行 geth 子命令 init 初始化創世區塊。.net
geth --datadir $HOME/deepeth init genesis.json
執行成功後,即可啓動該私有鏈:
geth --maxpeers 0 --datadir $HOME/deepeth console
執行以下命令,能夠查看到前面建立的兩個帳戶,均已有資產:
eth.getBalance(eth.accounts[0]) // 18446744073709551615 eth.getBalance(eth.accounts[1]) // 18446744073709551615
至此,咱們已完成創世定製版。
上面我已完成自定義創世,但以太坊做爲去中心平臺,須要許多節點一塊兒參與。僅僅爲了測試,多個節點來搭建私有鏈比較麻煩。若是但願和別人一塊兒聯調,或者須要在測試網絡中測試DAPP時,該怎麼辦呢?那麼,可以使用以太坊測試網絡。以太坊公開的測試網絡有 5 個,目前仍在運行的有 4 個,具體見下表格。
測試網 | 共識機制 | 出塊間隔 | 提供方 | 上線時間 | 備註 | 狀態 | |
---|---|---|---|---|---|---|---|
Morden | PoW | 以太坊官方 | 2015.7 | 因難度炸彈被迫退役 | stopped | ||
Ropsten | PoW | 30秒 | 以太坊官方 | 2016.11 | 接替Morden | running | |
Kovan | PoA | 4秒 | 以太坊錢包Parity開發團隊 | 2017.3 | 不支持geth | running | |
Rinkeby | PoA | 15秒 | 以太坊官方 | 2017.4 | 最經常使用,只支持geth | running | |
Sokol | PoA | 5秒 | 以太坊官方POA.network團隊 | 2017.12 | 不支持geth | running | |
Görli | PoA | 15秒 | 以太坊柏林社區 | 2018.9 | 首個以太坊2.0實驗場 | running |
支持 geth 的3個測試網絡的創世配置已內置在以太坊代碼中,具體見 core/genesis.go
文件:
// DefaultTestnetGenesisBlock returns the Ropsten network genesis block. func DefaultTestnetGenesisBlock() *Genesis{} // DefaultRinkebyGenesisBlock returns the Rinkeby network genesis block. func DefaultRinkebyGenesisBlock() *Genesis // DefaultGoerliGenesisBlock returns the Görli network genesis block. func DefaultGoerliGenesisBlock() *Genesis{}
固然不會缺以太坊主網創世配置,也是 geth 運行的默認配置。
// DefaultGenesisBlock returns the Ethereum main net genesis block. func DefaultGenesisBlock() *Genesis{}
若是你不想自定義創世配置文件用於開發測試,那麼以太坊也提供一份專用於本地開發的配置。
// DeveloperGenesisBlock returns the 'geth --dev' genesis block. Note, this must // be seeded with the func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis
運行 geth --dev console
可臨時運行使用。但若是須要長期使用此模式,則須要指定 datadir
。
geth --dev --datadir $HOME/deepeth/dev console
首次運行 dev 模式會自動建立一個空密碼的帳戶,並開啓挖礦。當有新交易時,將馬上打包出塊。
在運行 geth 時需根據配置文件加載創世配置以及創世區塊,並校驗其合法性。若是配置信息隨意變動,易引發共識校驗不經過等問題。只有在加載並檢查經過時,才能繼續運行程序。
上圖是一個簡要流程,下面分別講解「加載創世配置」和「安裝創世區塊」兩個子流程。
應使用哪一種創世配置,由用戶在啓動 geth 時決定。下圖是創世配置選擇流程圖:
經過 geth 命令參數可選擇不一樣網絡配置,能夠經過 networkid
選擇,也可以使用網絡名稱啓用。
geth 啓動時根據不一樣參數選擇加載不一樣網絡配置,並對應不一樣網絡環境。若是不作任何選擇,雖然在此不會作出選擇,但在後面流程中會默認使用主網配置。
上面已初步選擇創世配置,而這一步則根據配置加載或者初始化創世單元。下圖是處理流程:
首先,須要從數據庫中根據區塊高度 0 讀取創世區塊哈希。若是不存在則說明本地屬於第一次啓動,直接使用運行時創世配置來構建創世區塊。屬於首次,還須要存儲創世區塊和鏈配置。
若是存在,則須要使用運行時創世配置構建創世區塊並和本次已存儲的創世區塊哈希進行對比。一旦不一致,則返回錯誤,不得繼續。
隨後,還須要檢查鏈配置。先從數據庫獲取鏈配置,若是不存在,則無需校驗直接使用運行時鏈配置。不然,須要檢查運行時鏈配置是否正確,只有正確時才能替換更新。但有一個例外:主網配置不得隨意更改,由代碼控制而非人爲指定。
總的來講,以太坊默認使用主網配置,只有在首次運行時才建立和存儲創世區塊,其餘時候僅僅用於校驗。而鏈配置除主網外則在規則下可隨時變動。
上面咱們已知曉整體流程,這裏再細說下以太坊是如何根據創世配置生成創世區塊。核心代碼位於 core/genesis.go:229
。
func (g *Genesis) ToBlock(db ethdb.Database) *types.Block{ if db == nil { db = rawdb.NewMemoryDatabase() } statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))//❶ for addr, account := range g.Alloc { //❷ statedb.AddBalance(addr, account.Balance) statedb.SetCode(addr, account.Code) statedb.SetNonce(addr, account.Nonce) for key, value := range account.Storage { statedb.SetState(addr, key, value) } } root := statedb.IntermediateRoot(false)//❸ head := &types.Header{//❹ Number: new(big.Int).SetUint64(g.Number), Nonce: types.EncodeNonce(g.Nonce), Time: g.Timestamp, ParentHash: g.ParentHash, Extra: g.ExtraData, GasLimit: g.GasLimit, GasUsed: g.GasUsed, Difficulty: g.Difficulty, MixDigest: g.Mixhash, Coinbase: g.Coinbase, Root: root, } //❺ if g.GasLimit == 0 { head.GasLimit = params.GenesisGasLimit } if g.Difficulty == nil { head.Difficulty = params.GenesisDifficulty } statedb.Commit(false)//❻ statedb.Database().TrieDB().Commit(root, true)//❼ return types.NewBlock(head, nil, nil, nil)//❽ }
上面代碼是根據創世配置生成創世區塊的代碼邏輯,細節以下:
state
(後續文章會詳細講解 state
對象)。❷ 遍歷配置中 Alloc
項帳戶集合數據,直接寫入 state 中。
這裏不單能夠設置 balance
,還能夠設置 code
、nonce
以及任意多個 storage
數據。
意味着創世時即可以直接部署智能合約。例以下面配置則在創世時部署了一個名爲093f59f1d91017d30d8c2caa78feb5beb0d2cfaf
的智能合約。
"alloc": { "093f59f1d91017d30d8c2caa78feb5beb0d2cfaf": { "balance": "0xffffffffffffffff", "nonce": "0x3", "code":"0x606060", "storage":{ "11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa":"1234ff" } } }
StateRoot
。Root
字段中。GasLimit
和 Difficulty
直接影響到下一個區塊出塊處理。❽ 利用區塊頭建立區塊,且區塊中無交易記錄。
深刻淺出區塊鏈 - 系統學習區塊鏈,學區塊鏈都在這裏,打造最好的區塊鏈技術博客。