以太坊如何搭建私有連聯盟鏈
如何啟動geth節點對大家來說已經不是什麼難事,今天博主就帶大家學習一下如何搭建兩個節點的聯盟鏈。
私有鏈的建立
在之前的文章中我們已經講到過私有鏈的建立,本篇文章我們會有道私有鏈建立的知識,就重新溫故一下。建立私有鏈首先需要指定創始塊的配置,也就是genesis.json的配置。此檔案就是一個內容格式為json的文字檔案。
配置檔案的內容格式基本如下:
{
"alloc": {},
"config": {
"chainID": 72,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"nonce ": "0x0000000000000000",
"difficulty": "0x4000",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
"gasLimit": "0xffffffff"
}
配置項簡介
我們對配置項的內容進行一下簡單的介紹。
alloc: 用來預置賬號以及賬號的以太幣數量,因為私有鏈挖礦比較容易,所以不需要預置有幣的賬號,需要的時候自己建立即可以。例項程式碼如下:
"alloc": {
"de1e758511a7c67e7db93d1c23c1060a21db4615": {
"balance": "1000"
},
"27dc8de9e9a1cb673543bd5fce89e83af09e228f": {
"balance": "1100"
},
"d64a66c28a6ae5150af5e7c34696502793b91ae7": {
"balance": "900"
}
nonce:一個64位隨機數,用於挖礦,和mixhash的設定需要滿足以太坊的Yellow paper, 4.3.4.Block Header Validity (44)章節所描述的條件。
difficulty: 設定計算區塊的難度,如果數值過大,挖礦時間較長,在測試環境為節省算力和等帶時間可設定較小值。
mixhash:與nonce配合用於挖礦,由上一個區塊的一部分生成的hash。和nonce的設定需要滿足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章節所描述的條件。
coinbase: 礦工賬號,隨便填寫。
timestamp: 設定創世塊的時間戳。
parentHash: 上一個區塊的hash值,因為是創世塊,所以這個值是0。
extraData: 附加資訊,隨便填,可以填你的個性資訊,必須為十六進位制的字串。
gasLimit: 該值設定對GAS的消耗總量限制,用來限制區塊能包含的交易資訊總和,因為是私有鏈,所以填最大。
創世塊初始化
本教程以mac作業系統為例,其他作業系統可對照執行。
執行以下命令來啟動初始化創世塊的命令,最簡單的一組操作就是制定dir檔案路徑,和初始化檔案目錄。前面我們已經寫好了genesis.json的配置檔案,下面就執行一下初始化操作,涉及到操作引數為init。
ershixiongdeMacBook-Pro:geth zzs$ ./geth --datadir ./data-init1/ init genesis.json
本教程將初始化json檔案放在geth同級目錄下,如果放在其他目錄下,指定具體的路徑即可。同時建立了一個data-init1目錄專門儲存節點資料,執行完成會發現在該目錄下多出兩個目錄,一個為geth一個為keystore。其中geth裡面放資料相關資訊,keystore裡面放加密過的私鑰檔案。
執行時列印日誌如下:
WARN [12-28|19:12:03] No etherbase set and no accounts found as default
INFO [12-28|19:12:03] Allocated cache and file handles database=/Users/zzs/develop/eth/geth/data-init1/geth/chaindata cache=16 handles=16
INFO [12-28|19:12:03] Writing custom genesis block
INFO [12-28|19:12:03] Successfully wrote genesis state database=chaindata hash=942f59…a2588a
INFO [12-28|19:12:03] Allocated cache and file handles database=/Users/zzs/develop/eth/geth/data-init1/geth/lightchaindata cache=16 handles=16
INFO [12-28|19:12:03] Writing custom genesis block
INFO [12-28|19:12:03] Successfully wrote genesis state database=lightchaindata hash=942f59…a2588a
經過以上命令,我們已經完成了私有連的初始化工作。因為我們要建立聯盟鏈,因此需要再建立執行一遍同樣的命令,json檔案必須相同,datadir目錄必須不同。博主使用data-init2目錄來儲存第二個節點的資料。
ershixiongdeMacBook-Pro:geth zzs$ ./geth --datadir ./data-init2/ init genesis.json
啟動並進入控制檯
根據具體的作業系統,開兩個視窗來啟動兩個節點。這裡有一點需要注意的是,雖然是兩個節點,但他們的啟動程式都是geth,只不過datadir目錄不同而已。
在第一個視窗執行以下命令啟動一個節點,注意啟動之後不要關閉視窗。
./geth --datadir ./data-init1/ --networkid 88 --nodiscover console
引數簡介:
networkid 指定網路ID,確保不適用1-4。
nodiscover 此引數確保geth不去尋找peers,主要是為了嚴格控制聯盟鏈連入的節點。
這裡我們需要注意的是在啟動第一個節點時並沒有指定port引數,因此此處採用了預設的port,也就是30303。
以下為執行時列印的日誌,並進入控制檯。通過日誌我們也可以發現埠為30303。
WARN [12-28|19:23:16] No etherbase set and no accounts found as default
INFO [12-28|19:23:16] Starting peer-to-peer node instance=Geth/v1.7.3-stable-4bb3c89d/darwin-amd64/go1.9.2
INFO [12-28|19:23:16] Allocated cache and file handles database=/Users/zzs/develop/eth/geth/data-init2/geth/chaindata cache=128 handles=1024
WARN [12-28|19:23:16] Upgrading database to use lookup entries
INFO [12-28|19:23:16] Database deduplication successful deduped=0
INFO [12-28|19:23:16] Initialised chain configuration config="{ChainID: 72 Homestead: 0 DAO: <nil> DAOSupport: false EIP150: <nil> EIP155: 0 EIP158: 0 Byzantium: <nil> Engine: unknown}"
INFO [12-28|19:23:16] Disk storage enabled for ethash caches dir=/Users/zzs/develop/eth/geth/data-init2/geth/ethash count=3
INFO [12-28|19:23:16] Disk storage enabled for ethash DAGs dir=/Users/zzs/.ethash count=2
INFO [12-28|19:23:16] Initialising Ethereum protocol versions="[63 62]" network=88
INFO [12-28|19:23:16] Loaded most recent local header number=0 hash=942f59…a2588a td=16384
INFO [12-28|19:23:16] Loaded most recent local full block number=0 hash=942f59…a2588a td=16384
INFO [12-28|19:23:16] Loaded most recent local fast block number=0 hash=942f59…a2588a td=16384
INFO [12-28|19:23:16] Regenerated local transaction journal transactions=0 accounts=0
INFO [12-28|19:23:16] Starting P2P networking
INFO [12-28|19:23:16] RLPx listener up self="enode://aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b4098238324[email protected][::]:30303?discport=0"
INFO [12-28|19:23:16] IPC endpoint opened: /Users/zzs/develop/eth/geth/data-init2/geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/v1.7.3-stable-4bb3c89d/darwin-amd64/go1.9.2
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
> INFO [12-28|19:23:18] Mapped network port proto=tcp extport=30303 intport=30303 interface="UPNP IGDv1-IP1"
下面在另外一個視窗,換一個埠,比如30306,再換一下datadir,來啟動第二個節點。
./geth --datadir ./data-init2/ --port 30306 --networkid 88 --nodiscover console
執行上面命令,完成節點2的啟動,並進入控制檯。
新增coinbase賬戶
上面的日誌我們也看到警告資訊,提示沒有賬戶存在,那麼現在我們就在第一個節點上建立一個賬戶,具體在控制檯操作命令如下:
> personal.listAccounts
[]
> personal.newAccount("123456")
"0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e"
>
上面的命令先是查看了節點下的地址,結果為空,然後建立了一個祕密為123456的賬號。
同樣的,在另外一個視窗我們執行同樣的命令:
> personal.listAccounts
[]
> personal.newAccount("123456")
"0x02b7344004c45465796f779b7b95d7912c2ef572"
>
這樣,兩個節點就擁有了兩個地址。同時,在它們的keystore目錄下對應生成了加密的私鑰檔案。
我們也可以再次執行list命令檢視新增賬戶之後的情況。同時可以執行以下命令檢視coinbase賬號:
> eth.coinbase
"0x02b7344004c45465796f779b7b95d7912c2ef572"
>
由於只有一個地址,因此該地址就作為coinbase地址。如果想檢視更多的資訊可以執行以下命令:
> personal.listWallets
[{
accounts: [{
address: "0x02b7344004c45465796f779b7b95d7912c2ef572",
url: "keystore:///Users/zzs/develop/eth/geth/data-init2/keystore/UTC--2017-12-28T11-36-18.185974427Z--02b7344004c45465796f779b7b95d7912c2ef572"
}],
status: "Locked",
url: "keystore:///Users/zzs/develop/eth/geth/data-init2/keystore/UTC--2017-12-28T11-36-18.185974427Z--02b7344004c45465796f779b7b95d7912c2ef572"
}]
這裡不僅列印了賬戶資訊,還打印出了私鑰儲存的位置和賬戶狀態等資訊。
聯盟鏈互通
上面分別是在兩個節點上進行的操作,下面我們需要把兩個節點之間建立起連結。首先,我們執行以下命令檢視以下節點的peers的情況。
> admin.peers
[]
發現節點並沒有連結上任何其他節點,這也是我們的nodiscover引數發揮了效果。
下面就通過分享enode地址的方式來讓兩個節點建立連結。
> admin.nodeInfo.enode
"enode://aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b4098238324[email protected][::]:30306?discport=0"
>
通過上面命令,我們獲得了節點2的encode資訊。這是geth用來連線到不同節點的enode資訊,在這些不同的節點它們能夠分享交易和成功挖掘資訊。
其實這個資訊如果留心的話,在啟動節點的列印日誌中已經打印出每個節點的encode資訊。比如:
INFO [12-28|19:23:16] RLPx listener up self="enode://aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b4098238324[email protected][::]:30303?discport=0"
現在,我們要告知一個節點,另外一個節點的encode資訊。首先複製節點2的日誌中self等號後面的資訊,在節點1的控制檯執行以下命令:
> admin.addPeer("enode://aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b4098238324[email protected][::]:30306?discport=0")
true
返回true,說明執行成功。再次驗證一下:
> admin.peers
[{
caps: ["eth/63"],
id: "aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b40982383240a05b680060ce8b0ce020a96c49c9c2c3628c4ea3281845211bd4cf4f03b35c",
name: "Geth/v1.7.3-stable-4bb3c89d/darwin-amd64/go1.9.2",
network: {
localAddress: "[::1]:49426",
remoteAddress: "[::1]:30306"
},
protocols: {
eth: {
difficulty: 16384,
head: "0x942f596f99dc8879b426b59080824662e1f97587353d087487fea0a0e2a2588a",
version: 63
}
}
}]
>
發現節點1已經有一個peer了,同時我們可以看到remoteAddress: “[::1]:30306”,正是我們節點2的埠資訊。在節點2執行admin.peers會發現有類似的資訊,指向的peer正是節點1的。
查詢餘額並挖礦
執行檢視餘額命令:
> eth.getBalance(eth.coinbase)
0
>
發現兩個節點的賬號餘額都為0。
在節點1執行miner.start()進行挖礦,執行miner.stop()停止挖礦。停止挖礦的時候開業忽略控制檯輸出,只要正確拼寫命令回車即可。
當我們在節點1執行挖礦時,我們會發現節點2的控制檯出現了這樣的日誌資訊:
> INFO [12-28|20:05:32] Block synchronisation started
INFO [12-28|20:05:33] Imported new state entries count=1 elapsed=47.661µs processed=1 pending=0 retry=0 duplicate=0 unexpected=0
WARN [12-28|20:05:33] Discarded bad propagated block number=1 hash=ab49ba…1cf32f
INFO [12-28|20:05:33] Imported new block headers count=2 elapsed=9.208ms number=2 hash=738225…000e3b ignored=0
INFO [12-28|20:05:33] Imported new chain segment blocks=2 txs=0 mgas=0.000 elapsed=1.724ms mgasps=0.000 number=2 hash=738225…000e3b
INFO [12-28|20:05:33] Fast sync complete, auto disabling
INFO [12-28|20:05:34] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=5.978ms mgasps=0.000 number=3 hash=b069a9…426060
INFO [12-28|20:05:38] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=6.930ms mgasps=0.000 number=4 hash=21217e…526253
INFO [12-28|20:05:41] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=6.419ms mgasps=0.000 number=5 hash=3fa6ff…cf2794
INFO [12-28|20:05:43] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=6.557ms mgasps=0.000 number=6 hash=4c35b9…78b3ec
INFO [12-28|20:05:45] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=6.514ms mgasps=0.000 number=7 hash=328e62…1bd3d3
INFO [12-28|20:05:46] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=6.513ms mgasps=0.000 number=8 hash=12287e…0465b5
INFO [12-28|20:06:19] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=7.048ms mgasps=0.000 number=9 hash=8e844b…b99d6c
INFO [12-28|20:06:22] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=8.156ms mgasps=0.000 number=10 hash=159b36…d4dde5
INFO [12-28|20:06:24] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=6.549ms mgasps=0.000 number=11 hash=969100…5658a5
也就是說,節點1挖礦,節點2在同步資料資訊。
停止節點1的挖礦,並檢視coinbase地址金額:
> miner.stop()
true
> eth.getBalance(eth.coinbase)
140000000000000000000
> eth.coinbase
"0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e"
這裡我們知道了節點一種的地址資訊和餘額資訊,那我們拿節點1的這個地址在節點2的控制檯查詢一下資訊:
> eth.getBalance("0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e")
140000000000000000000
>
很顯然,節點2中也能查詢到節點1中地址的餘額。以上資訊說明,節點1和節點2的資料是完全同步的。
交易轉賬
現在我們從節點1的coinbase賬戶發一筆交易到節點2的coinbase賬戶。
> personal.unlockAccount("0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e")
Unlock account 0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e
Passphrase:
true
> eth.sendTransaction({from: eth.coinbase, to: '0x02b7344004c45465796f779b7b95d7912c2ef572', value: 100000000})
INFO [12-28|20:13:21] Submitted transaction fullhash=0xc32e40f0f606a6368aa2c4c6a27db20f5a1d728fb9ef1f50a0410f4889e095a0 recipient=0x02b7344004c45465796F779B7b95d7912C2eF572
"0xc32e40f0f606a6368aa2c4c6a27db20f5a1d728fb9ef1f50a0410f4889e095a0"
>
解鎖賬戶併發送1幣交易資訊。現在檢視一下節點2的地址內是否有餘額:
> eth.getBalance("0x02b7344004c45465796f779b7b95d7912c2ef572")
0
>
發現餘額是0,為什麼呢?因為雖然我們發起了交易,單並沒有礦工挖礦打包交易。再次執行miner.start()。再次查詢,就會發現節點2的coinbase地址已經有金額了。
> eth.getBalance("0x02b7344004c45465796f779b7b95d7912c2ef572")
100000000
通過以上的操作我們已經建立了一個擁有兩個節點的聯盟鏈,如果想建立更多節點的聯盟鏈,可以此新增新的節點。
注意事項
需要注意的是,如果一個節點重啟,則需要重新新增peer。另外,在實際生產中可去掉console命令,通過之前介紹的attach命令進入控制檯。
更多資訊
或許更多相關資訊可關注微信公眾號:《程式新視界》和知識星球(小密圈),為你解疑答惑。也可加入QQ技術群(659809063)進行交流。