使用Geth 構建以太坊區塊鏈並模擬挖礦過程
Go-ethereum 是以太坊官方的一個Golang 實現,我們可以使用Geth 工具來創建創世區塊並啟動區塊鏈,使用Clef 實現以太坊錢包的功能,以及使用evm、puppeth 用來以太坊開發。本次我們使用Geth,在兩台Host 上,創建一條以太坊區塊鏈和兩個賬戶,並進行區塊鏈的一些基本操作。
我的網絡環境中,Host1 的IP 地址為192.168.0.100,Host2 的IP 地址為192.168.0.180
本文絕大多數參考資料來源於Geth Documentation,在官方的文檔里可以找到諸如Geth 安裝使用、如何產生私有區塊鏈、JSON-RPC APIs 的使用方法等,
1. 獲得 Geth
Geth 在各個平台都可用,直接從https://geth.ethereum.org/docs/install-and-build/installing-geth 獲取即可
在Windows 上,安裝Geth,我們會獲得幾個可執行程序:
Directory: C:\Users\sun\Desktop\geth
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2020/12/11 16:17 50280980 abigen.exe
-a--- 2020/12/11 16:17 48783481 bootnode.exe
-a--- 2020/12/11 16:17 63922102 clef.exe
-a--- 2020/12/11 16:18 49921889 evm.exe
-a--- 2020/12/11 16:18 67456679 geth.exe
-a--- 2020/12/11 16:18 26165842 puppeth.exe
-a--- 2020/12/11 16:18 2512896 rlpdump.exe
其中Geth 理論上已經被寫進Path,在任何位置打開shell 都可用。
2. 准備
新建一個privatechain1文件夾,用來保存我們第一個區塊鏈。geth需要一個創世配置文件genisis.json
,來創建區塊鏈,例如:
{
"config": {
"chainId": 10086,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"ethash": {}
},
"difficulty": "1",
"gasLimit": "80000",
"alloc": {}
}
此處我們定義chainID 為10086,使用PoW 共識機制,挖礦難度為1,沒有初始資產
有關genesis.json 的詳細格式可以參考:創世區塊配置文件genesis.json的格式解讀
3. 啟動
創建好genesis.json后,就可以根據該配置創建區塊鏈:
geth init --datadir . genesis.json
--datadir
參數指定區塊鏈相關文件的放置目錄
隨后啟動區塊鏈:
geth --networkid 191 --nodiscover --http --http.addr "0.0.0.0" --http.port 8545 --http.api personal,eth,net,web3 --allow-insecure-unlock --datadir . console
啟動參數較多,分別為:
--networkid
網絡ID。只有當NetworkID,ChainID,創世區塊全部相同時,才是同一條區塊鏈
--nodiscover
由於本次為實驗環境,而且只有兩台Host,我們可以關閉自動發現,手動添加Peers
--http
該區塊鏈使用http協議。除了http,還可以使用websocket
--http.addr
監聽地址,由於之后要在兩台Host上傳輸數據,因此此處為0.0.0.0
--http.port
默認端口為8545
--http.api
可以允許使用http進行的一些操作,后續會詳解
--allow-insecure-unlock
由於在本地做測試,為了方便不會配置https證書,加上這個參數可以在非HTTPS的環境下解鎖賬戶
--datadir
指定區塊鏈數據所在的目錄
console
打開控制台
有關詳細的Geth 啟動參數,可以參閱Geth Command-line Options
啟動成功后,會打開一個web3控制台,類似這樣:
在Host2上重復上述操作,啟動相同的區塊鏈。
4. 操作
4.1 新用戶
此時geth的任務已完成,我們開始使用 JSON RPC APIs 進行操作。
> eth.accounts
[]
可以看到目前還沒有任何賬戶,新建一個賬戶:
輸入personal.newAccount()
,然后輸入兩次密碼即可
可以看到我們的新賬戶地址為:0xf1d65f66658a3dfa86b689ce084fd3234fc3b691
,並且在keystore目錄下生成了一個文件。
再次查看eth.accounts
,剛剛創建的新用戶已出現:
> eth.accounts
["0xf1d65f66658a3dfa86b689ce084fd3234fc3b691"]
在Host2上重復上述操作,創建另一個新用戶:
==============================================
Shell on Host2
==============================================
> eth.accounts
["0xf228360fbf872e4aca7f930a60b18c6f1f6ca19e"]
4.2 添加Peers
盡管兩台Host現在運行同一條區塊鏈,但由於我們預先關閉了peers自動發現,因此目前並沒有Peers。在Host1上查看:
> admin.peers
[]
要添加在Host1上添加Host2,我們需要得到Host2的網絡地址。在Host2的控制台中向上翻,找到區塊鏈啟動時,出現的一個Strated P2P networking
信息:
INFO [01-26|01:02:40.182] Started P2P networking self="enode://c81e35213c6fd2824dafdd174b24aa0bb36e7ce935819d2ecf22a19a53aea5e760caf32e20bf3818ab129eb89efaa33ed841a542cd7ea973b60e2b95ef3953c5@127.0.0.1:30303?discport=0"
其中self的值就是Host2的網絡地址,注意應該將127.0.0.1改為192.168.0.180
enode://c81e35213c6fd2824dafdd174b24aa0bb36e7ce935819d2ecf22a19a53aea5e760caf32e20bf3818ab129eb89efaa33ed841a542cd7ea973b60e2b95ef3953c5@192.168.0.180:30303?discport=0
回到Host1,執行以下操作:
admin.addPeer("enode://c81e35213c6fd2824dafdd174b24aa0bb36e7ce935819d2ecf22a19a53aea5e760caf32e20bf3818ab129eb89efaa33ed841a542cd7ea973b60e2b95ef3953c5@192.168.0.180:30303?discport=0")
一段時間后,發現peercount=1,再次查看peers
如果此步遲遲不能發現新Peer,可以嘗試在Host2上添加Host1
4.3 挖礦
在以太坊中,每挖出一個新區塊,礦工將會獲得一定的獎勵。獎勵將會流入coinbase賬戶中。如果genesis.json沒有指定默認的coinbase賬戶,則會流入eth.accounts[0]
中。下面我們開始模擬挖礦過程。
要使用某一個賬戶進行挖礦、交易等操作,首先需要解鎖賬戶,方便起見,給Host1上的第一個賬戶地址設一個變量sender:
> sender = eth.accounts[0]
"0xf1d65f66658a3dfa86b689ce084fd3234fc3b691"
> personal.unlockAccount(sender)
Unlock account 0xf1d65f66658a3dfa86b689ce084fd3234fc3b691
Passphrase:
true
然后開始挖礦:
miner.start(1)
其中參數1指進程數,可按Host性能設置。由於我們在genesis.json中將difficulty設置得很小,理論上不會消耗很多時間。挖礦開始后,不斷提示Generating DAG in progress,當percentage到達100%時,會看到挖掘出新區塊。
Host1會將挖到的區塊作為交易銷售出去,在區塊鏈上留下交易痕跡
來到Host2,會發現Host1提交的新區塊被同步
適時停止挖礦:
miner.stop()
在Host1上查看賬戶sender的賬戶余額:
> miner.stop()
null
> eth.getBalance(sender)
34000000000000000000
查看目前區塊鏈上挖出區塊數量,在Host1和Host2上理論上應該出現相同的結果:
> eth.blockNumber
17
這個數字和之前顯示的INFO最后一個挖出的區塊number對應:
INFO [01-26|12:34:00.697] 🔨 mined potential block number=17 hash="5ac61c…ee83ca"
4.4 轉賬
Host1上的第一個賬戶0xf1d65f66658a3dfa86b689ce084fd3234fc3b691
,變量名為sender
Host2上的第一個賬戶0xf228360fbf872e4aca7f930a60b18c6f1f6ca19e
,設置新的變量名為receiver
> receiver = "0xf228360fbf872e4aca7f930a60b18c6f1f6ca19e"
設置轉賬的amount,轉出1個ETH,在以太坊上進行交易,需要將ETH 單位轉為Wei 單位:
> amount = web3.toWei(1, "ether")
執行sendTransaction 操作:
> eth.sendTransaction({from: sender, to: receiver, value: amount})
如果出現Error: authentication needed: password or unlock,可能時時間過長,sender 賬戶又被加鎖,重新解鎖即可。
sendTransaction 執行之后,Host2 中的receiver 賬戶並不會立刻受到1ETH(即1000000000000000000Wei),之前提到過,要在區塊鏈中留下數據,必須通過挖礦產生新區塊,提交到區塊鏈中。因此要使轉賬生效,我們需要開始挖礦,將交易數據打包到新區塊中。
在此之前,先來看一下等待隊列中的交易,輸入eth.pendingTransactions
:
其中包含兩個新參數gas 和gasPrice,是需要付給確認這筆交易的礦工的工資。
開始挖礦:
> miner.start(1)
挖到第一個區塊就可以停止挖礦了:
> miner.stop()
在Host1 上挖到了新區塊:
INFO [01-26|13:59:12.443] 🔨 mined potential block number=18 hash="675837…edf7ca"
INFO [01-26|13:59:13.060] Successfully sealed new block number=19 sealhash="58e4be…89b373" hash="15d1f0…7ca475" elapsed=638.999ms
INFO [01-26|13:59:13.060] 🔗 block reached canonical chain number=12 hash="7c0bcf…76cec9"
INFO [01-26|13:59:13.088] Commit new mining work number=20 sealhash="18f1a8…de77c4" uncles=0 txs=0 gas=0 fees=0 elapsed=28.006ms
在Host2 上找到這個已經同步來的區塊:
INFO [01-26|13:59:13.690] Imported new chain segment blocks=1 txs=1 mgas=0.021 elapsed=7.991ms mgasps=2.628 number=18 hash="675837…edf7ca" dirty=4.72KiB
此時在Host2 上查看receiver 的余額:
> receiver = "0xf228360fbf872e4aca7f930a60b18c6f1f6ca19e"
> eth.getBalance(receiver)
1000000000000000000
在Host1 上查看sender 余額:
> eth.getBalance(sender)
43000000000000000000
5. 總結
該篇章中我們使用Geth 生成了一條區塊鏈,並在兩個Host 上,用兩個用戶完成了挖礦和轉賬操作。下一篇章我們會着重理解以太坊的工作方式,入門智能合約。
作為以太坊初學者,本文很可能有概念理解上的錯誤,歡迎各位批評指正。
📖其他參考資料:
區塊鏈入門(3):在以太坊私有網絡中建立節點集群,並發生交易 - 88911562 - 博客園 (cnblogs.com)
私有鏈挖礦的時候,這個generating DAG好慢 有什么方法加快這個速度-鏈客區塊鏈技術開發者社區 (liankexing.com)