go-ethereum 私有鏈搭建指南
前言
工具
以太坊客戶端
以太坊客戶端用於接入以太坊網絡,進行賬戶管理、交易、挖礦、智能合約相關的操作。目前有多種語言實現的客戶端,常用的有 Go 語言實現的 go-ethereum 客戶端 Geth,支持接入以太坊網絡併成爲一個完整節點,也可作爲一個 HTTP-RPC 服務器對外提供 JSON-RPC 接口。
其他的客戶端有:
- Parity:Rust 語言實現;
- cpp-ethereum:C++ 語言實現;
- ethereumjs-lib:JavaScript 語言實現;
- Ethereum(J):Java 語言實現;
- ethereumH:Haskell 語言實現;
- pyethapp: Python 語言實現;
- ruby-ethereum:Ruby 語言實現;
智能合約編譯器
以太坊支持兩種智能合約的編程語言:Solidity 和 Serpent。Serpent 語言面臨一些安全問題,現在已經不推薦使用了。Solidity 語法類似 JavaScript,它編譯器 solc 可以把智能合約源碼編譯成以太坊虛擬機 EVM 可以執行的二進制碼。
現在以太坊提供更方便的在線 IDE —— Remix https://remix.ethereum.org 使用 Remix,免去了安裝 solc 和編譯過程,它可以直接提供部署合約所需的二進制碼和 ABI。以太坊錢包
以太坊提供了圖形界面的錢包 Ethereum Wallet 和 Mist Dapp 瀏覽器。錢包的功能是 Mist 的一個子集,可用於管理賬戶和交易;Mist 在錢包基礎上,還能操作智能合約。爲了演示合約部署過程,本文使用了 Geth console 操作,沒有用到 Mist,當然,使用 Mist 會更簡單。
環境說明:
操作系統:Ubuntu16.4
Go環境:1.9.2
安裝 ethereum
1、PPA 直接安裝
# 安裝必要的工具包
apt install software-properties-common
# 添加以太坊源
add-apt-repository -y ppa:ethereum/ethereum
apt update
# 安裝 go-ethereum
apt install ethereum
安裝完成可以通過geth version
命令查看是否安裝成功
2、源碼安裝,源碼安裝需要安裝Go環境
參考官方文檔 https://golang.org/doc/install
- 配置Go環境,版本1.9.2
# 下載最新版本
curl -O https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz
# 解壓
tar -C /usr/local -xzf go1.9.2.linux-amd64.tar.gz
# 卸載
apt-get purge golang-go
# 查看版本
go version
設置GOPATH和PATH
1、設置一個去文件夾
mkdir -p ~/go; echo "export GOPATH=$HOME/go" >> ~/.bashrc
2、更新你的路徑
echo "export PATH=$PATH:$HOME/go/bin:/usr/local/go/bin" >> ~/.bashrc
3、將環境變量讀入當前會話:
source ~/.bashrc
安裝ethereum
sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo add-apt-repository -y ppa:ethereum/ethereum-dev
sudo apt-get update
sudo apt-get install ethereum
- 安裝 solc 編譯器
sudo add-apt-repository ppa:ethereum/ethereum-qt
sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install cpp-ethereum
至此環境已經安裝完成
私有鏈搭建
一、配置初始狀態
要運行以太坊私有鏈,需要定義自己的創世區塊,創世區塊信息寫在一個 JSON 格式的配置文件中。首先將下面的內容保存到一個 JSON 文件中,例如 genesis.json
。
{
"config": {
"chainId": 1024,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"alloc": {},
"nonce": "0x0000000000000042",
"difficulty": "0x020000",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x6d6f7475692d32",
"gasLimit": "0xffffffff"
}
各個參數的含義如下:
chainId :指定了獨立的區塊鏈網絡 ID。網絡 ID 在連接到其他節點的時候會用到,以太坊公網的網絡 ID 是 1,爲了不與公有鏈網絡衝突,運行私有鏈節點的時候要指定自己的網絡 ID。不同 ID 網絡的節點無法相互連接。
mixhash:與nonce配合用於挖礦,由上一個區塊的一部分生成的hash。注意他和nonce的設置需要滿足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章節所描述的條件。.
- nonce: nonce就是一個64位隨機數,用於挖礦,注意他和mixhash的設置需要滿足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章節所描述的條件。
- difficulty: 設置當前區塊的難度,如果難度過大,cpu挖礦就很難,這裏設置較小難度
- alloc: 用來預置賬號以及賬號的以太幣數量,因爲私有鏈挖礦比較容易,所以我們不需要預置有幣的賬號,需要的時候自己創建即可以。默認爲空即可
- coinbase: 礦工的賬號,隨便填
- timestamp: 設置創世塊的時間戳
- parentHash: 上一個區塊的hash值,因爲是創世塊,所以這個值是0
- extraData: 附加信息。【注意】新版本該值需要爲16進制數據,以
0x
開頭 - gasLimit: 該值設置對GAS的消耗總量限制,用來限制區塊能包含的交易信息總和,因爲我們是私有鏈,所以填最大。
二、初始化:寫入創世區塊
初始化命令如下。/root/chain
【{dataDir}】目錄存放區塊鏈數據(可爲其他目錄)
geth --datadir "/root/chain" init genesis.json
出現如下內容就成功了
Successfully wrote genesis state database=lightchaindata hash=84e71d…97246e
初始化成功後,會在數據目錄{dataDir} 中生成 geth
和 keystore
(存儲加密後的賬戶信息)兩個文件夾,目錄結構如下:
.(root)
├── chain
│ ├── geth
│ │ ├── chaindata
│ │ │ ├── 000001.log
│ │ │ ├── CURRENT
│ │ │ ├── LOCK
│ │ │ ├── LOG
│ │ │ └── MANIFEST-000000
│ │ └── lightchaindata
│ │ ├── 000001.log
│ │ ├── CURRENT
│ │ ├── LOCK
│ │ ├── LOG
│ │ └── MANIFEST-000000
│ └── keystore
└── genesis.json
三、啓動私有鏈節點
初始化完成後,就有了一條自己的私有鏈,之後就可以啓動自己的私有鏈節點並做一些操作,在終端中輸入以下命令即可啓動節點:
geth --identity "motui" --rpc --rpccorsdomain "*" --datadir "/root/chain" --port "30303" --rpcapi "db,eth,net,web3" --networkid 89898 console
參數說明如下:
- identity :指定節點 用於標示目前網絡的名字;
- rpc :表示開啓 HTTP-RPC 服務;
- init :指定創世塊文件的位置,並創建初始塊
- rpcport :指定 HTTP-RPC 服務監聽端口號(默認爲 8545);
- rpcapi: 設置允許連接的rpc的客戶端,一般爲db,eth,net,web3
- datadir :指定區塊鏈數據的存儲位置;
- port :指定和其他節點連接所用的端口號(默認爲 30303);
- networkid: 設置當前區塊鏈的網絡ID,用於區分不同的網絡,是一個數字
- nodiscover :關閉節點發現機制,防止加入有同樣初始配置的陌生節點。
- maxpeers : 如果你不希望其他人連接到你的測試鏈,可以使用maxpeers 0。反之,如果你確切知道希望多少人連接到你的節點,你也可以通過調整數字來實現。
- rpccorsdomain : 這個可以指示什麼URL能連接到你的節點來執行RPC定製端任務。務必謹慎,輸入一個特定的URL而不是wildcard ( * ),後者會使所有的URL都能連接到你的RPC實例。
- console:啓動命令行模式,可以在Geth中執行命令
注意:如果想將Ubuntu作爲永久區塊鏈節點使用,當使用
nohup
命令時,Geth啓動參數console
必須去掉,否則Geth會自動停止。
啓動完成之後進入控制檯,這是一個交互式的 JavaScript 執行環境,在這裏面可以執行 JavaScript 代碼,其中 >
是命令提示符。在這個環境裏也內置了一些用來操作以太坊的 JavaScript 對象,可以直接使用這些對象。這些對象主要包括:
- eth:包含一些跟操作區塊鏈相關的方法;
- net:包含一些查看p2p網絡狀態的方法;
- admin:包含一些與管理節點相關的方法;
- miner:包含啓動&停止挖礦的一些方法;
- personal:主要包含一些管理賬戶的方法;
- txpool:包含一些查看交易內存池的方法;
- web3:包含了以上對象,還包含一些單位換算的方法。
> eth.accounts
["0x6594cc2f72908c0fea54d5c9dd297ce68f735411", "0x442aa25d86f30c3ed1cdee8cb787dccac680abdd"]
>
3、查看賬戶餘額
> eth.getBalance(eth.accounts[0])
0
> eth.getBalance(eth.accounts[1])
0
>
查詢餘額有多種方式,可以通過創建時的16進制碼,也可以通過賬戶下標查詢,下標默認從0開始
啓動&停止挖礦
啓動挖礦
> miner.start(1)
其中 start 的參數表示挖礦使用的線程數。第一次啓動挖礦會先生成挖礦所需的 DAG 文件,這個過程有點慢,等進度達到 100% 後,就會開始挖礦,此時屏幕會被挖礦信息刷屏。
停止挖礦
> miner.stop()
挖到一個區塊會獎勵n個以太幣,挖礦所得的獎勵會進入礦工的賬戶,這個賬戶叫做 coinbase,默認情況下 coinbase 是本地賬戶中的第一個賬戶,可以通過 miner.setEtherbase() 將其他賬戶設置成 coinbase。
交易
假如0賬戶向1賬戶轉賬,需要先把0賬戶鎖定,然後才能進行交易
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x6594cc2f72908c0fea54d5c9dd297ce68f735411
Passphrase:
true
>
轉賬 賬戶0 –> 賬戶1
> amount = web3.toWei(5,'ether')
"5000000000000000000"
> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:amount})
INFO [01-18|17:56:14] Submitted transaction fullhash=0x065c698cceaf996ab9ab06c6eddc8b035704defbcff0d65227d32c06a5b17637 recipient=0x4BD9F7b3c365D35C50757AF4CC7Bdb1307A083C0
"0x065c698cceaf996ab9ab06c6eddc8b035704defbcff0d65227d32c06a5b17637
>
此時如果沒有挖礦,用 txpool.status
命令可以看到本地交易池中有一個待確認的交易,可以使用 eth.getBlock("pending", true).transactions
查看當前待確認交易。
查交易和區塊
> eth.blockNumber
200
通過交易 Hash 查看交易(Hash 值包含在上面交易返回值中):
> eth.getTransaction("0x65a8278d571d7cf3f2ca36ce721900d61e9d6eadd1ed5f24f39b646c2b194427")
{
blockHash: "0x0101b5f9e9c50ee45156f0631df18a97176aef5215bde40b3e167195d367ed80",
blockNumber: 342,
from: "0x16671cdabbc9b3f0f1b31380d972dcd9725f7d8a",
gas: 90000,
gasPrice: 18000000000,
hash: "0x65a8278d571d7cf3f2ca36ce721900d61e9d6eadd1ed5f24f39b646c2b194427",
input: "0x",
nonce: 1,
r: "0x52b7fce530ebd67527d4fe684cb3b5a4b96909154aeedcc5f371ab27e9e7f1ba",
s: "0x141268e8fef639b79773f6ab7fb778b958be77bd05944f305dc4f1ec68f21962",
to: "0x4bd9f7b3c365d35c50757af4cc7bdb1307a083c0",
transactionIndex: 0,
v: "0x823",
value: 5000000000000000000
}
>
通過區塊號查看區塊:
> eth.getBlock(0)
{
difficulty: 131072,
extraData: "0x6d6f7475692d32",
gasLimit: 4294967295,
gasUsed: 0,
hash: "0x5117726df3040cb5cbd6e8d59cdaacaaa1cd8df4a20072d0e8242e96ea5c893f",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x0000000000000000000000000000000000000000",
mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
nonce: "0x0000000000000042",
number: 0,
parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 515,
stateRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
timestamp: 0,
totalDifficulty: 131072,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: []
}
>
連接到其他節點
所有的節點創建都是一樣的。
可以通過 admin.addPeer()
方法連接到其他節點,兩個節點要要指定相同的 chainID。
假設有兩個節點:節點一和節點二,chainID 都是 1024,通過下面的步驟就可以從節點一連接到節點二。
首先要知道節點二的 enode 信息,在節點二的 JavaScript console 中執行下面的命令查看 enode 信息:
> admin.nodeInfo.enode
"enode://8db747ce768cb5e6ea3dc00bd16fe2bdf56dbeda0b1abfcd2ddcbb5a5cf0fe6bb0123ca341935c2c4348a34c89b0a73e2fac72e8b4fac414006760aa9ab1f613@[::]:30303"
>
然後在節點一的 JavaScript console 中執行 admin.addPeer(),就可以連接到節點二:
> admin.addPeer("enode://8db747ce768cb5e6ea3dc00bd16fe2bdf56dbeda0b1abfcd2ddcbb5a5cf0fe6bb0123ca341935c2c4348a34c89b0a73e2fac72e8b4fac414006760aa9ab1f613@127.0.0.1:30303")
true
>
addPeer() 的參數就是節點二的 enode 信息,注意要把 enode 中的 [::]
替換成節點二的 IP 地址。連接成功後,節點二就會開始同步節點一的區塊,同步完成後,任意一個節點開始挖礦,另一個節點會自動同步區塊,向任意一個節點發送交易,另一個節點也會收到該筆交易。
通過 admin.peers
可以查看連接到的其他節點信息,通過 net.peerCount
可以查看已連接到的節點數量。
除了上面的方法,也可以在啓動節點的時候指定 --bootnodes
選項連接到其他節點。
注意:
1、對於操作過程中存在的問題網上基本上都有解決方法,可以參考錯誤信息自己解決
2、如果使用的服務器或者虛擬機需要注意防火牆和端口的配置