NO IMAGE

寫在前面

寫這個主要是為了記錄下自己的學習過程,同時如果能幫助到同樣想搭建私有鏈的朋友們,那是再好不過了

Step 1 環境搭建

私鏈搭建有三寶,環境,終端和錢包。我這裡用到的是Geth客戶端,所以環境當然就是指Go語言執行環境。Ethereum的終端(客戶端)有很多語言(C ,Python…bala..bala)的實現版本,這裡我用的是Go語言的實現版本,也是使用較多的版本,這裡就隨個大流,畢竟用的越多,資料越豐富[1]
– Go環境
Go環境的安裝還是算方便的,在不用理解各種目錄的情況下,直接下載客戶端安裝好就ok了。以前Go是被牆了的,不過現在谷歌推出了中國開發者的官網,但是我進去後也沒看到下載。為了方便大家,我在網盤儲存了一份,大家可以下載,Windows版密碼:jq7a,Linux版密碼:ngbp,Mac版密碼:lavf。想要了解具體Go環境安裝及其目錄關係,大家可以自行搜尋。

  • Geth客戶端
    Geth的安裝很簡單。
    Windows的使用者很方便,直接下載客戶端即可。不過貌似被牆了,我這裡提前下載過一個,放在了百度網盤。雖然個人很不喜歡流氓網盤,因為不買會員,下載速度奇慢,百兆寬頻也枉然,不過也實在沒啥好地方放。大家可以下載安裝,密碼:zzch
    Mac,Ubuntu的同學也可以方便的安裝端執行以下命令即可完成安裝,FreeBSD等其餘Linux版本的同學可以下載原始碼編譯安裝。
Mac同學
brew tap ethereum/ethereum
brew install ethereum
//開發版的安裝可以加上 --devel引數(如下),我沒加,直接用的上面的命令,二選一即可吧
brew install ethereum --devel
Ubuntu同學
sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum
  • Mist或Ethereum錢包
    我用的Mist錢包原始碼,及Ethereum的錢包安裝包。錢包的安裝包安裝方式與之前無異,這個安裝比較簡單,下載對應平臺的安裝包即可。我也提供我放到百度網盤的Ethereum錢包安裝包。Mac安裝包下載密碼:qfug,Linux的deb安裝包下載密碼:8jvr,Windows安裝包下載密碼:epul
    我是下載Mist錢包原始碼,然後安裝了開發環境的。事實證明,如果不是非要弄山寨幣修改錢包,還是不要折騰原始碼錢包,直接用安裝包裝Ethereum錢包。如果對自家網路比較有信心,不妨一試,我反正執行命令後,去城市郊區玩兒了兩天回來,親眼見證了安裝完成的最後一刻。
    開發環境安裝[2]
    先安裝Node.js環境,我選擇的推薦的8.9.4LTS安裝
    然後依次執行下面的命令,安裝依賴(我知道,你肯定會直接拷貝命令的,別把$也拷貝下來了):
//安裝依賴:
$ curl https://install.meteor.com/ | sh
$ curl -o- -L https://yarnpkg.com/install.sh | bash
$ yarn global add [email protected]
$ yarn global add gulp
//下載錢包原始碼並執行,相信我,你一定會看見錢包連線節點的介面!
$ git clone https://github.com/ethereum/mist.git
$ cd mist
$ yarn

此時,三個小時已過去,一下午沒了…下載上傳,碼字不易。不過這一切是值得的,你即將執行以太坊,激不激動!我反正已經肝兒顫了。接下來,進入主題,執行以太坊私有鏈!

Step2 修改創世塊

以太坊,比特幣等的區塊鏈都是從創世塊開始的(你可以簡單理解成連結串列的頭結點),創世塊是要手動配置後生成的。下面是創世塊的配置檔案(也就是一個Json檔案)。修改好後儲存為genesis.json即可。 當然,你想換個canglaoshi.json也沒人說什麼[1]

{
"config": {
//區塊鏈的ID,你隨便給一個就可以
"chainId": 21,
//下面三個引數暫時不知道幹啥的
//等我知道了補上,或者有哪位大神知道
//可以在評論裡指點我,謝謝
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
//用來預置賬號以及賬號的以太幣數量,應該也就是所謂的預挖
//我這裡不需要預挖,所以給了個空物件
//如果需要可以這樣加
//"alloc": {
//"0x0000000000000000000000000000000000000001": {"balance": "111111111"},
//"0x0000000000000000000000000000000000000002": {"balance": "222222222"}
//}
"alloc"      : {},
//幣基地址,也就是預設的錢包地址,因為我沒有地址,所以全0,為空
//後面執行Geth後建立新賬戶時,如果Geth發現沒有幣基地址,會預設將第一個賬戶的地址設定為幣基地址
//也就是礦工賬號
"coinbase"   : "0x0000000000000000000000000000000000000000",
//挖礦難度,你可以隨便控制哦,這裡設定的難度比較小,因為我喜歡錢來得快
"difficulty" : "0x4000",
//附加資訊,隨便填個文字或不填也行,類似中本聰在比特幣創世塊中寫的報紙新聞
"extraData"  : "",
//gas最高限制,以太坊執行交易,合約等所消耗的gas最高限制,這裡設定為最高
"gasLimit"   : "0xffffffff",
//64位隨機數,用於挖礦,注意他和mixhash的設定需要滿足以太坊黃皮書中的要求
//直接用我這個也可以
"nonce"      : "0x0000000000000042",
//與nonce共同用於挖礦,注意他和nonce的設定需要滿足以太坊黃皮書中的要求
"mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
//上一個區塊的Hash值,因為是創世塊,石頭裡蹦出來的,沒有在它前面的,所以是0
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
//創世塊的時間戳,這裡給0就好
"timestamp"  : "0x00"
}

Step3 開啟你的Geth客戶端,給它找點事做

修改好創世塊的Json檔案後,我們就可以利用它來建立私有鏈了。建立一個資料夾來存放你的創世塊檔案。我這裡就叫eth_test,裡面放著我的創世塊Json檔案image.png

接下來,開啟終端,輸入下面這個命令[3][7]

//--datadir 後面跟的eth的工作目錄,你隨便給一個資料夾就行,區塊的資料會存在這個資料夾裡
// init 後面跟的引數是genesis.json檔案所在位置。我是在genesis.json檔案所在的目錄開啟的終端,所以不需要給genesis.json的路徑,給出檔名即可
geth --datadir "your/ethdata/filelocation" init your/genesis.json/loaction

image.png

如果你指定的目錄下面出現了紅框的資料夾,終端中出現Successfully wrote 等資訊,恭喜你,創世塊建立完成!

然後我們開啟一個Geth節點,輸入下面的命令:

geth --datadir "/Users/guojh/Documents/ethTestFiles/eth_test" --identity "Guo Chain" --networkid 19900418 --port 61916 --rpcport 8206 console
//--datadir 後面跟的是你指定的工作目錄
//--identity 後面跟的是你的區塊鏈標識,隨便寫
//--networkid 後面跟的是你的網路id,這個是區別區塊鏈網路的關鍵
//--port 和 --rpcport 你隨便給一個就行,別跟在用的埠重複就行

如果你得到的結果如下圖,說明你成功開啟了Geth的節點,並進入JavaScript終端。注意箭頭,第一個箭頭位置就是創世塊中配置的chainId。最後一條INFO告訴你ipc檔案位置,這個後面會用到。

image.png

進入JavaScript終端後,你可以輸入下面三個命令,建立一個賬戶。在建立賬戶之前,coinbase地址是空的,建立完賬戶後,coinbase為剛才建立的賬戶地址。

//建立一個新賬戶
personal.newAccount("123456")
//user1變數儲存剛才建立的賬戶,可以看出,eth.accounts陣列存放了賬戶地址
user1 = eth.accounts[0]
//解鎖剛才建立的賬戶,如果不解鎖,不能轉賬
//Geth隔一段時間就會鎖定賬戶,所以需要解鎖
personal.unlockAccount(user1, "123456")
//檢視coinbase
eth.coinbase

image.png

輸入命令檢視賬戶餘額
eth.getBalance(user1)
可以看到賬戶餘額為0
image.png

現在,開始為自己賺錢吧~ 輸入挖礦命令:
miner.start()
如果你看到終端不停有輸出,那就對了。如果想停止挖礦,輸入停止命令:
miner.stop()
在輸入的時候你會發現輸入的文字被列印出的log打亂了,不用擔心,輸你的,不影響。此時再檢視餘額,你變成富翁了!
到這裡,我想到個問題,現在沒有任何交易,區塊裡也沒有任何交易資訊,這也能得到以太幣獎勵?後來在Gitter的go-ethereum討論組中諮詢得知,只要是能產生區塊,就有獎勵,即使區塊中沒有任何有用資訊。
image.png

Step4 多節點測試

只有一個節點略顯孤單,我們再建立一個節點,讓他倆有情人終成眷屬 :)。這裡我們會在建立節點命令中增加一個引數 bootnodes,在建立節點的同時,讓新節點連線上剛才建立的節點。bootnodes跟的引數是節點地址。如果沒有加bootnodes也不怕,建立好節點後呼叫admin.addPeer(“enode”),將enode替換成節點地址即可。

將Step3中開啟節點的命令替換成下面的命令(這裡的genesis.json和第一個節點的必須一樣,否則就是兩個鏈了。另外,兩個埠號不要和第一個節點重複,工作目錄也不要重複,但是networkid必須一致):
略有點長

geth --datadir "your/ethdata/filelocation" init your/genesis.json/loaction
geth --datadir "your/ethdata/filelocation" --identity "Guo Chain" --networkid 19900418 --port 61917 --rpcport 8207 --bootnodes "enode://40fadf14ab5084f03dcea80f1380e60ce270d423f45e1ba71e37ba892d9822bb0[email protected]192.168.3.32:61916" console

這裡的enode可以在第一次建立的節點中輸入:
admin.nodeInfo.enode
終端會顯示出節點enode資訊,用你的本機IP替換[::]
image.png

同樣看到下圖,進入JavaScript控制檯,就是看到親人了
image.png

但是如何驗證兩個節點連線上了呢?見證奇蹟的時刻到了。在新節點中建立賬戶,建立完成後,看!發現沒,節點在同步區塊資料了!說明兩個節點連上了!
image.png

Step5 連線錢包

在終端中進入之前下載好的Mist錢包的原始碼檔案目錄中。現在就要用到之前啟動節點時建立的ipc檔案了

  • 首先來說說使用Ethereum錢包的連線方式[4][5]
    我使用的是Mac,就是在終端輸入下面的命令,給錢包一個rpc引數,其餘平臺應該也類似,就是通過終端啟動錢包,並提供引數(geth.ipc檔案就是你啟動完節點後,自動生成的,就在節點目錄下,錢包連線到私有鏈需要提供這個檔案,否則會連線到主鏈上):
open -a /Applications/Ethereum\ Wallet.app/ --args --rpc /Users/guojh/Documents/ethTestFiles/eth_test/geth.ipc

image.png
瞬間發現我還挺有錢的。注意到那個紅框了嗎,說明確實連線到的是私有鏈。

接下來我們可以轉賬了!
在另一個節點檢視地址(可以直接輸入user1檢視),拷貝下來地址,貼上到錢包中轉賬
image.png
image.png
點選send,輸入密碼,轉賬完成!
去另外一個節點終端檢視,卻發現餘額是0
image.png
這是怎麼回事呢?朋友,不要忘了,交易是需要礦工確認的。礦工在哪裡呢?礦工就是你自己。交易通知到P2P網路中的節點,但是沒有礦工確認交易,所以交易沒有執行。我們現在有兩個節點,隨便哪個開啟挖礦,就能確認交易。當然,也可以玩兒玩兒,兩個節點同時開啟挖礦,看看誰能搶先確認交易。你可以看到,這邊在挖礦,錢包就收到了確認交易的訊息

這時,再檢視另一個節點的餘額,竊喜吧,朋友,你有錢了
image.png

  • 下面說下Mist錢包原始碼方式
    輸入下面的命令:
yarn dev:electron --rpc /Users/guojh/Documents/ethTestFiles/eth_test/geth.ipc --node-ipcpath /Users/guojh/Documents/ethTestFiles/eth_test/geth.ipc

敲擊回車,你有可能看到如下介面

這就略顯尷尬了,一片空白。咋辦呢,我猜想可能是區塊同步有問題,要不開開採礦試試看錢包連上沒。結果就連上了!激動啊!但人就是賤啊,我想看看是不是挖礦就一定能連上,立馬關閉錢包,再試一次,然後悲劇了,從此以後,Mist錢包就打不開了,每次都是一閃而過,就消失了,終端提示視窗被關閉。有沒有哪位大神知道原因?
image.png
過了很久之後我又連線上過一次,錢包操作方法和Ethereum錢包一樣,然後就又打不開了,本文的遺憾…後面我找到原因再來補充更新這個地方

如果不想用錢包,也可以使用命令來轉賬,你需要輸入from,轉賬來源,to,轉賬目的地址,value,轉賬金額,這裡把1個ether轉成以太幣最小單位Wei來傳送
eth.sendTransaction({from: "0x5fba50fce50baf0b8a7314200ba46336958ac97e", to: "0x0a8c35653d8b229c16f0c9ce6f63cffb877cfdcf", value: web3.toWei(1, "ether")})
回車後開啟挖礦,一樣可以轉賬。

Step6 建立你的代幣

在以太坊上建立代幣很簡單,但是這種代幣的交易是基於以太坊,也就是交易費還是要用以太幣支付。如果需要修改礦工獎勵,有自己的錢包等,還是需要修改以太坊原始碼的,這裡我先介紹最簡單的代幣建立[6]

為了簡便,我們在私有鏈上建立代幣,跟在以太坊主鏈上建立代幣是一樣的操作方法。按照Step5的方法開啟錢包(Mist或者Ethereum錢包都可以,看哪個你能開啟…),連線到你的私有鏈上。
點選右上方的合約按鈕(CONTRACTS),然後點選部署新合約(DEPLOY NEW CONTRACT)
image.png

將下面的程式碼拷貝到SOLIDITY CONTRACT SOURCE CODE編輯框中(編輯框中預設的程式碼全部刪除)

pragma solidity ^0.4.16;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
contract TokenERC20 {
// Public variables of the token
string public name;
string public symbol;
uint8 public decimals = 18;
// 18 decimals is the strongly suggested default, avoid changing it
uint256 public totalSupply;
// This creates an array with all balances
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
// This generates a public event on the blockchain that will notify clients
event Transfer(address indexed from, address indexed to, uint256 value);
// This notifies clients about the amount burnt
event Burn(address indexed from, uint256 value);
/**
* Constrctor function
*
* Initializes contract with initial supply tokens to the creator of the contract
*/
function TokenERC20(
uint256 initialSupply,
string tokenName,
string tokenSymbol
) public {
totalSupply = initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
balanceOf[msg.sender] = totalSupply;                // Give the creator all initial tokens
name = tokenName;                                   // Set the name for display purposes
symbol = tokenSymbol;                               // Set the symbol for display purposes
}
/**
* Internal transfer, only can be called by this contract
*/
function _transfer(address _from, address _to, uint _value) internal {
// Prevent transfer to 0x0 address. Use burn() instead
require(_to != 0x0);
// Check if the sender has enough
require(balanceOf[_from] >= _value);
// Check for overflows
require(balanceOf[_to]   _value > balanceOf[_to]);
// Save this for an assertion in the future
uint previousBalances = balanceOf[_from]   balanceOf[_to];
// Subtract from the sender
balanceOf[_from] -= _value;
// Add the same to the recipient
balanceOf[_to]  = _value;
Transfer(_from, _to, _value);
// Asserts are used to use static analysis to find bugs in your code. They should never fail
assert(balanceOf[_from]   balanceOf[_to] == previousBalances);
}
/**
* Transfer tokens
*
* Send `_value` tokens to `_to` from your account
*
* @param _to The address of the recipient
* @param _value the amount to send
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
/**
* Transfer tokens from other address
*
* Send `_value` tokens to `_to` on behalf of `_from`
*
* @param _from The address of the sender
* @param _to The address of the recipient
* @param _value the amount to send
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(_value <= allowance[_from][msg.sender]);     // Check allowance
allowance[_from][msg.sender] -= _value;
_transfer(_from, _to, _value);
return true;
}
/**
* Set allowance for other address
*
* Allows `_spender` to spend no more than `_value` tokens on your behalf
*
* @param _spender The address authorized to spend
* @param _value the max amount they can spend
*/
function approve(address _spender, uint256 _value) public
returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
/**
* Set allowance for other address and notify
*
* Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it
*
* @param _spender The address authorized to spend
* @param _value the max amount they can spend
* @param _extraData some extra information to send to the approved contract
*/
function approveAndCall(address _spender, uint256 _value, bytes _extraData)
public
returns (bool success) {
tokenRecipient spender = tokenRecipient(_spender);
if (approve(_spender, _value)) {
spender.receiveApproval(msg.sender, _value, this, _extraData);
return true;
}
}
/**
* Destroy tokens
*
* Remove `_value` tokens from the system irreversibly
*
* @param _value the amount of money to burn
*/
function burn(uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value);   // Check if the sender has enough
balanceOf[msg.sender] -= _value;            // Subtract from the sender
totalSupply -= _value;                      // Updates totalSupply
Burn(msg.sender, _value);
return true;
}
/**
* Destroy tokens from other account
*
* Remove `_value` tokens from the system irreversibly on behalf of `_from`.
*
* @param _from the address of the sender
* @param _value the amount of money to burn
*/
function burnFrom(address _from, uint256 _value) public returns (bool success) {
require(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
require(_value <= allowance[_from][msg.sender]);    // Check allowance
balanceOf[_from] -= _value;                         // Subtract from the targeted balance
allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
totalSupply -= _value;                              // Update totalSupply
Burn(_from, _value);
return true;
}
}

image.png

接下來選擇Token ERC 20
image.png

你會看見下面出現了3個輸入框,填入對應資訊
image.png

下一步,選擇手續費,這個看你了,不過肯定是越高,礦工處理速度越快(在主鏈要注意這點,我們現在是私有鏈,無所謂),點選DEPLOY,輸入你賬戶的密碼。
image.png
image.png
在主鏈上部署時要注意,有時候會提示你部署錯誤,通常原因都是你手續費給的不夠,調高一點點手續費吧。另外,我還碰到過交易提交了,但是沒有任何礦工處理我的交易,幾天都如此,開始我很鬱悶,不敢再部署,怕多給錢。不過後面等不下去了,又建了一個,手續費調高了一點,馬上就處理了。目測要麼是手續費太低,沒礦工處理,要麼是以太坊擁堵(直到現在一個月過去了,還是沒處理,估計廢了)。

最後一步,開啟挖礦,處理自己的交易。其實可以不用等到12個確認,有一個確認,你的交易就被處理了。挖礦完成後,再次點選合約(CONTRACTS),看,代幣做好了,200w個!
image.png

你現在是代幣的發行者了!我們試著把代幣轉一些給我們的另一個節點。
點選錢包的傳送(SEND),輸入另一個節點的地址,輸入轉賬金額,先轉它10w個,幣太多,沒辦法。下面注意了,前面轉賬的時候,只有ETHER,現在多了個剛才建立的代幣,毫不猶豫選擇它(注意下,千萬不要把主鏈上的以太幣轉到私有鏈的錢包地址,否則你的以太幣就消失在茫茫區塊鏈中了)。
image.png

同樣,選擇手續費額度,點選傳送(SEND),輸入密碼後,開啟挖礦處理交易。交易處理完後,再看我們的CONTRACTS裡的JHCoin,少了10w個
image.png
現在請點選JHCOIN(也就是你的代幣),拷貝你的代幣地址
image.png

我們可以把錢包連線到另一個節點(如果錢包老是在連線節點中,開啟錢包連線節點的挖礦程式,一下就能連上),發現並沒有看到我們剛才轉的代幣,怎麼回事呢?是這樣,錢包不會自動識別新代幣,要手動新增後,才能顯示,這就是為什麼要拷貝代幣地址的原因。
還是點選錢包右上方的合約(CONTRACTS)按鈕,點選最下面的關注代幣(亂翻譯的…原文是WATCH TOKEN)
image.png

將剛才拷貝的代幣地址貼上過來,點選OK
image.png
再看看,10w代幣到賬!
image.png

到此為止,1天過去了,不易啊,腰都坐酸了。

寫在後面

寫這篇教程,或者說我自己搭建的時候,搜尋了很多資料,總結出一個經驗,官方文件或者Github上,都會給出最基本,最簡單的操作方法,結合網友們的文章看,更容易搭建起來。
在搭建過程中,你會更加具體的感受到區塊鏈的工作方式,我覺得還是很有助於理解以太坊或其它基於區塊鏈技術的專案。
有興趣的可以加群討論,一起學習 701477586

經作者同意,轉載自簡書