以太坊私鏈初探

最近在學(xué)習(xí)區(qū)塊鏈的技術(shù),初步打算從go-ethereum入手岳守,學(xué)習(xí)一下以太坊的設(shè)計(jì)思想順便把GoLang入個(gè)門(簡單凄敢、直接明了,是目前這個(gè)階段需要追求的東西)湿痢。

手上有一個(gè)臺阿里云的服務(wù)器(單核1G)涝缝,性能一般,平時(shí)就用來掛著自己的個(gè)站,工作不飽和拒逮,打算在上面搭一條以太坊的私鏈罐氨,方便學(xué)習(xí)。

環(huán)境

服務(wù)器:

  • 單核CPU滩援,1G內(nèi)存栅隐,40G磁盤
  • OS:CentOS
  • OS內(nèi)核:3.10.0-693.11.1.el7.x86_64
  • 軟件包管理工具:yum

go-ethereum源碼下載

以太坊的節(jié)點(diǎn)有兩個(gè)版本,基于c++編寫的cpp-ethereum和基于go編寫的go-ethereum狠怨,這里選擇go語言版本约啊,主要還是希望能順便熟悉一下這門語言
選擇一個(gè)最新的release版本1.8,下載到服務(wù)器佣赖,位置隨意恰矩,這里我們選擇放在/usr/src(存放源碼)下。

cd /usr/src/
wget https://github.com/ethereum/go-ethereum/archive/release/1.8.zip
1521190470392.jpg

解壓zip包得到我們需要的源碼文件

unzip 1.8.zip
1521195373874.jpg

安裝go語言環(huán)境

yum install golang
1521195682289.jpg

編譯go-ethereum源碼

cd go-ethereum-release-1.8/
make geth
1521197127296.jpg

到這里我們就得到了需要的以太坊節(jié)點(diǎn)程序/go-ethereum-release-1.8/build/bin/geth
可以把編譯好的程序拷貝到/usr/bin目錄下憎蛤,方便運(yùn)行

創(chuàng)建初始區(qū)塊

以太坊私鏈的初始區(qū)塊需要手動創(chuàng)建起來外傅,否則整個(gè)區(qū)塊鏈沒法持續(xù)運(yùn)行,我們從官網(wǎng)得到一段初始配置

{
  "config": {
        "chainId": 0,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
  "alloc"      : {},
  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x20000",
  "extraData"  : "",
  "gasLimit"   : "0x2fefd8",
  "nonce"      : "0x0000000000000042",
  "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00"
}

創(chuàng)建一個(gè)文件俩檬,并復(fù)制初始配置進(jìn)去

touch init.json

接下來就可以完成初始區(qū)塊的配置了

geth  --datadir chaindata  init init.json

--datadir 參數(shù)指定區(qū)塊鏈的數(shù)據(jù)存儲位置萎胰,可以根據(jù)需求自己指定

啟動區(qū)塊鏈并開放RPC接口

geth --rpc --rpcaddr "*.*.*.*" --rpccorsdomain "*" --datadir "chaindata" --rpcport "8545" --rpcapi "db,eth,net,web3" --networkid 31415926 console 2>>log.txt

--rpc 啟動RPC協(xié)議
--rpcaddr 指定服務(wù)器ip地址
--rpccorsdomain 設(shè)置允許訪問的域名
--datadir 區(qū)塊鏈數(shù)據(jù)存儲位置,要與初始化時(shí)保持一致
--rpcport PRC協(xié)議接口
--rpcapi RPC支持的API
--networkid 區(qū)塊鏈網(wǎng)絡(luò)ID棚辽,用于發(fā)現(xiàn)節(jié)點(diǎn)
最后把日志重定向到log.txt文件中技竟,方便我們查看

當(dāng)然還需要去阿里云的服務(wù)器配置中把TCP的8545端口打開


1521198807402.jpg

到這里節(jié)點(diǎn)就已經(jīng)啟動了

創(chuàng)建賬戶并啟動挖礦

創(chuàng)建賬戶,指定密碼
personal.newAccount('pwd')
綁定賬戶到挖礦程序
miner.setEtherbase(eth.accounts[0])
開啟挖礦屈藐,指定線程數(shù)
miner.start(2)
停止挖礦
miner.stop()

如果挖礦啟動失敗榔组,可以檢查一下是否綁定過賬戶miner.setEtherbase(eth.accounts[0])
這里miner.start(2)可以根據(jù)機(jī)器自身的性能指定需要啟動的線程數(shù)。但是我在啟動miner之后并沒有開始挖礦联逻,也沒有報(bào)錯(cuò)搓扯,仔細(xì)閱讀了以太坊的相關(guān)資料之后發(fā)現(xiàn)正常情況下以太坊節(jié)點(diǎn)是通過POW(proof-of-work)的方式產(chǎn)生新的區(qū)塊,如果機(jī)器性能比較低可能并不會產(chǎn)生新的區(qū)塊包归,或者生成新區(qū)塊的速度會非常慢锨推。

如果單純是為了測試開發(fā)使用,可以通過--dev參數(shù)初始化一條測試私鏈公壤。--dev參數(shù)會創(chuàng)建一個(gè)使用POA(proof-of-authority)的共識網(wǎng)絡(luò)换可,默認(rèn)預(yù)分配一個(gè)開發(fā)者賬戶并且會自動開啟挖礦。
可以通過下面的方式直接創(chuàng)建:

geth --rpc --rpcaddr "*.*.*.*" --rpccorsdomain "*" --datadir "chaindata" --rpcport "8545" --rpcapi "db,eth,net,web3" --networkid 31415926 --dev console 2>>log.txt

用pm2監(jiān)控geth

為了保證終端回話關(guān)閉之后geth還能正常運(yùn)行境钟,并能處理RPC請求必須要以守護(hù)進(jìn)程(daemon)的方式啟動锦担,這里有幾種方式:

  • nohup 命令
  • Systemd 工具
  • pm2 工具
    當(dāng)然還有其他的方式,這三種是我比較常用的慨削,因?yàn)闄C(jī)器上有個(gè)node的服務(wù)正在用pm2管理,這里也正好借用pm2工具管理一下geth
    首先創(chuàng)建一個(gè)pm2啟動配置文件:
touch start.json

配置參數(shù)
{
    "name": "geth",
    "script": "geth",
    "args": "--rpc --rpcaddr '*.*.*.*' --rpccorsdomain '*' --datadir 'chaindata' --rpcport '8545' --rpcapi 'db,eth,net,web3' --networkid 31415926 --dev",
    "log_date_format": "YYYY-MM-DD HH:mm Z",
    "merge_logs": false,
    "watch": false,
    "max_restarts": 10,
    "exec_interpreter": "none",
    "exec_mode": "fork_mode"
}

pm2 start start.json
1521259248462.png

geth以daemon的方式啟動并通過pm2進(jìn)行監(jiān)控,接下來就可以通過RPC的方式通信了

RPC通信

以太坊RPC的接口列表可以參考:https://ethereum.gitbooks.io/frontier-guide/content/rpc.html
這里遇到一個(gè)巨大的坑缚态,感謝安全工程師何處不可憐系統(tǒng)化的方法的幫助磁椒,讓我能一步步定位到問題。
我們以eth_accounts試一下:
因?yàn)榉?wù)器IP地址綁定了域名玫芦,然后調(diào)用方式可以這樣寫:

curl  -X POST --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":1}' http://domain:8545

然后就不正常了浆熔,直接報(bào)錯(cuò)

invalid host specified

感覺應(yīng)該是域名解析的問題,但是試了一下其他端口的服務(wù)一切正常桥帆,去DNS解析服務(wù)器看過也沒問題医增。然后更換服務(wù)端口依舊報(bào)錯(cuò)。似乎問題不在DNS解析和端口上
前端抓包顯示403老虫,
后端tcptump監(jiān)聽結(jié)果

14:54:50.651476 IP *.*.*.*.55747 > dawn.8545: Flags [P.], seq 2542259482:2542260075, ack 1963893059, win 4100, options [nop,nop,TS val 1412772142 ecr 4250673300], length 593
14:54:50.652009 IP dawn.8545 > *.*.*.*.55747: Flags [P.], seq 1:181, ack 593, win 255, options [nop,nop,TS val 4250708302 ecr 1412772142], length 180
14:54:50.657020 IP *.*.*.*.55747 > dawn.8545: Flags [.], ack 181, win 4094, options [nop,nop,TS val 1412772147 ecr 4250708302], length 0
14:54:50.685458 IP *.*.*.*.55747 > dawn.8545: Flags [P.], seq 593:1161, ack 181, win 4096, options [nop,nop,TS val 1412772176 ecr 4250708302], length 568
14:54:50.685546 IP dawn.8545 > *.*.*.*.55747: Flags [P.], seq 181:361, ack 1161, win 264, options [nop,nop,TS val 4250708336 ecr 1412772176], length 180
14:54:50.689829 IP *.*.*.*.55747 > dawn.8545: Flags [.], ack 361, win 4090, options [nop,nop,TS val 1412772179 ecr 4250708336], length

有來有往叶骨,似乎又沒啥問題咖楣。
修改請求地址仪或,換ip直接訪問量九,200寓盗,居然通了晒杈∪乘總結(jié)一下嘗試的結(jié)果:

  • DNS解析正常
  • 端口正常
  • IP訪問正常(服務(wù)正常)
  • 域名訪問異常

在服務(wù)正常DNS正常的情況想通過域名請求不到粱哼,問題很可能是服務(wù)本身的限制崎苗。參考wiki些阅,---rpccorsdomain參數(shù)配置為“*”沒有問題伞剑,允許所有域名訪問。然后再找似乎也沒有可用的參數(shù)了市埋。這個(gè)時(shí)候只能讀一下源碼黎泣,看看是不是能找到思路。之后就是找到go-ethereumRPC模塊的代碼腰素,一點(diǎn)點(diǎn)讀聘裁。最后發(fā)現(xiàn)了這么一段代碼

// ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler
func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // if r.Host is not set, we can continue serving since a browser would set the Host header
    if r.Host == "" {
        h.next.ServeHTTP(w, r)
        return
    }
    host, _, err := net.SplitHostPort(r.Host)
    if err != nil {
        // Either invalid (too many colons) or no port specified
        host = r.Host
    }
    if ipAddr := net.ParseIP(host); ipAddr != nil {
        // It's an IP address, we can serve that
        h.next.ServeHTTP(w, r)
        return

    }
    // Not an ip address, but a hostname. Need to validate
    if _, exist := h.vhosts["*"]; exist {
        h.next.ServeHTTP(w, r)
        return
    }
    if _, exist := h.vhosts[host]; exist {
        h.next.ServeHTTP(w, r)
        return
    }
    http.Error(w, "invalid host specified", http.StatusForbidden)
}
// DefaultConfig contains reasonable default settings.
var DefaultConfig = Config{
    DataDir:          DefaultDataDir(),
    HTTPPort:         DefaultHTTPPort,
    HTTPModules:      []string{"net", "web3"},
    HTTPVirtualHosts: []string{"localhost"},
    WSPort:           DefaultWSPort,
    WSModules:        []string{"net", "web3"},
    P2P: p2p.Config{
        ListenAddr: ":30303",
        MaxPeers:   25,
        NAT:        nat.Any(),
    },
}

到這里應(yīng)該差不多能猜到,有設(shè)置vhosts的地方弓千,然后用了一個(gè)比較笨的辦法

[dawn@dawn ~]$ geth --help|grep vhosts
  --rpcvhosts value      Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost")
[dawn@dawn ~]$

果然是有--rpcvhosts參數(shù)可以設(shè)置衡便,修改配置

"args": "--rpc --rpcaddr '*.*.*.*' --rpccorsdomain '*' --datadir 'chaindata' --rpcport '8545' --rpcapi 'db,eth,net,web3' --networkid 31415926 --dev --rpcvhosts '*'"

重啟服務(wù)

pm2 restart start.json

再次通過域名訪問,200洋访,果然通了镣陕,但是返回:

invalid content type, only application/json is supported

這就好處理了,增加content-type設(shè)置

請求
curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":1}' http://domain:8545
返回?cái)?shù)據(jù)
{"jsonrpc":"2.0","id":1,"result":["0x8018e73d7efc27297ea313e8bd250a02c6ca9f14","0xe3207f6fb2816fead3ccba99ebd2ea9f3ff22231"]}

到這里一切就都調(diào)通了姻政,后續(xù)的鏈上操作就可以通過RPC服務(wù)直接操作了呆抑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市汁展,隨后出現(xiàn)的幾起案子鹊碍,更是在濱河造成了極大的恐慌厌殉,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侈咕,死亡現(xiàn)場離奇詭異公罕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)耀销,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門楼眷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人熊尉,你說我怎么就攤上這事罐柳。” “怎么了狰住?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵张吉,是天一觀的道長。 經(jīng)常有香客問我转晰,道長芦拿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任查邢,我火速辦了婚禮蔗崎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扰藕。我一直安慰自己缓苛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布邓深。 她就那樣靜靜地躺著未桥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芥备。 梳的紋絲不亂的頭發(fā)上冬耿,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機(jī)與錄音萌壳,去河邊找鬼亦镶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛袱瓮,可吹牛的內(nèi)容都是我干的缤骨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼尺借,長吁一口氣:“原來是場噩夢啊……” “哼绊起!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起燎斩,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤虱歪,失蹤者是張志新(化名)和其女友劉穎蜂绎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體实蔽,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荡碾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年谨读,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了局装。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡劳殖,死狀恐怖铐尚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哆姻,我是刑警寧澤宣增,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站矛缨,受9級特大地震影響爹脾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜箕昭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一灵妨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧落竹,春花似錦泌霍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至积暖,卻和暖如春藤为,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背夺刑。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工缅疟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人性誉。 一個(gè)月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓窿吩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親错览。 傳聞我的和親對象是個(gè)殘疾皇子纫雁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容