以太坊私有鏈搭建指南
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環境
- 配置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://8db747ce768cb5e6ea3dc00bd16fe2bdf56dbeda0b1abfcd2ddcbb5a5cf0fe6bb[email protected][::]:30303"
>
然後在節點一的 JavaScript console 中執行 admin.addPeer(),就可以連線到節點二:
> admin.addPeer("enode://8db747ce768cb5e6ea3dc00bd16fe2bdf56dbeda0b1abfcd2ddcbb5a5cf0fe6bb[email protected]127.0.0.1:30303")
true
>
addPeer() 的引數就是節點二的 enode 資訊,注意要把 enode 中的 [::]
替換成節點二的 IP 地址。連線成功後,節點二就會開始同步節點一的區塊,同步完成後,任意一個節點開始挖礦,另一個節點會自動同步區塊,向任意一個節點發送交易,另一個節點也會收到該筆交易。
通過 admin.peers
可以檢視連線到的其他節點資訊,通過 net.peerCount
可以檢視已連線到的節點數量。
除了上面的方法,也可以在啟動節點的時候指定 --bootnodes
選項連線到其他節點。
注意:
1、對於操作過程中存在的問題網上基本上都有解決方法,可以參考錯誤資訊自己解決
2、如果使用的伺服器或者虛擬機器需要注意防火牆和埠的配置