python3
Ethereum
geth
ethminer
マイニング

Ethereum: geth ライブネット接続~ソロマイニング(GPU)するまでの話(2017年8月11日現在)

Ethereum: geth ライブネット接続~ソロマイニングするまでの話

(2017年8月11日現在)

環境
-os:Windows7
-メモリ:8GB
-グラフィックボード:gtx1060,rx580
-ソフトウェア:geth1.67, etherminer0.11,

やろうとしたこと
-Ethereumでライブネットと同期すること
-GPUでソロマイニングをしてみる。
-python3でjson-rpcを触ってみる

結果
-同期まで3日かかる。
-チェーンデータが32GB以上(2017/8/11時点)。
-ソロマイニングは結果を確認しきれない。
-python3で比較的簡単に情報が取り出せる。

1.Ethereumノードを構築する

gethバイナリ版をダウンロードする(現時点:geth1.67)
https://github.com/ethereum/go-ethereum/wiki/Installation-instructions-for-Windows

インストールを実行。

2.Ethereumのライブネットと同期を開始する 

gethをインストールしたら、コマンドライン上から実行。
(バッチファイルを作成しておくと楽。)

ethnode.bat
  geth.exe --datadir "D:\eth\mainnet" --fast --identity umidachi --rpc --rpcaddr "localhost" --rpcport "8545" --cache=2048 --ipcdisable console 

(オプション:--datadirは後々、チェーンサイズが大きくなることを想定して、容量に余裕のあるディレクトリを指定。)

コマンド投入直後、同期がすぐに完了しない。

同期の状況を確認するため、コンソール上からコマンドを投入

gethコンソール
 >eth.syncing

 currentBlock:××××
 highestBlock:4148728 ←2017年8月11日時点
 knownStates:××××
 pulledStates:××××
 startingBlock:××××

currentBlockがhighestBlockに近づけば同期する。
(currentBlockを400万ブロック以上になるまで気長に待つ。)

上記コマンド投入後、"false"が出力されることがあるが
同期が始まっていない可能性があるため、時間をおいて再度実行する。

ライブネット上の現在のブロックは次のサイトから確認できる。

https://etherscan.io/
https://ethstats.net/

ある程度、チェーンデータのダウンロードができると
eth.blockNumberで、自分のノードが持っているブロック番号を表示できる。

gethコンソール
 >eth.blockNumber

 4150662  (同期が進んでいなかったりすると0が出力される)

同期が完了すると次の画像のように、eth.blockNumberコマンドを投入したときの
blockNumberとetherscanのblockNumberが一致していることが確認できる。

無題.png

通信環境にも依存すると思うが、自分の場合3日経過して同期が完了した。
eth.syncingコマンド投入すると"false"が出力

gethコンソール
 >eth.syncing

 false

ノード用にETHを管理するアカウントを作る。

gethコンソール
 >personal.newAccount()

 Passphrase:

パスフレーズを求められるため、パスワード入力し
アカウントを作成する。

自身のアカウントが作成されたことを確認するため
次のコマンドを入力する。

gethコンソール
 >eth.coinbase
 "0x○○○○○○○○○○○○○" ←は自身で作成したアドレス

マイニングに成功した場合、"0x○○○○○○○○○○○○○"にEthが
入るはず。

3.マイニングツールをダウンロードする。

 下記ページからethminer 0.11.0 "Optimized Nvidia mining"のwindows版をダウンロードする。
https://github.com/ethereum-mining/ethminer/releases
 

ダウンロード完了後、ファイルを展開。
gethとは別にコマンドプロンプトを立ち上げて
ethminerを実行する。(バッチファイルを作っておくと楽)

gtx1060の場合
  ethminer.exe -U --cuda-devices 0
rx580の場合
  ethminer.exe  -G --opencl-platform 1 --opencl-devices 1

補足:
ethminerのオプションは適宜自身の環境に合わせて入力すること。
プールマイングは"-F"をつけてプール先のURLを指定するが、
ソロの場合-Fを指定しない。
(デフォルトでhttp://127.0.0.1:8545 となるため。)

上記コマンドを投入すると、gethコンソール上で次のメッセージが出力される。

INFO [08-13|10:53:13] Starting mining operation

gethのコンソール上で、マイニングが開始されているか確認をする。

gethコンソール
 >eth.mining
  true

マイニングのハッシュレートについてはgethのコンソール上で確認できる。

gethコンソール
  >eth.hashrate
  41201414  (gtx1060,rx580を同時に実行)

最後にgethのコンソール上で次のコマンドを投入し、ノードの残高を確認する。

gethコンソール
   eth.getBalance(eth.accounts[0])
   0

(執筆時点で残高が増えていることは未確認)

4.python3でノードと通信してみる。

これまでの同期状況やマイニングの状況を確認するために
gethコンソールでコマンドを投入するのも面倒。

python3にapiを叩くスクリプトを書いておき、ethereumノードと通信させる。

json-rpc用のモジュールを入れる。

コンソール
  pip3 install json-rpc

次のページにEthereumのjson-rpcの説明が載っているのでそれを参考にする。
https://github.com/ethereum/wiki/wiki/JSON-RPC

たとえば、blockNumberを出力させたいなら
次のsample.pyのようにする。

sample.py
import requests
import json

def eth_blockNumber(url):

    headers = {'content-type': 'application/json'}
    payload = {
        "jsonrpc": "2.0",
        "method": "eth_blockNumber",
        "params": [],
        "id": 0
    }

    response = requests.post(url, data=json.dumps(payload), headers=headers).json()
    print(response)
    print("bockNumber:",i)

if __name__ == "__main__":
    url="http://localhost:8545"
    eth_blockNumber(url)
出力
  {'jsonrpc': '2.0', 'id': 0, 'result': '0x3f56d1'}
  bockNumber: 4150993

出力からわかるように16進数で値が返されるため、10進数になおしている。

上記と同じ要領で、これまでに確認してきたコマンドを割り当てていく。

sample_json-rpc.py
import requests
import json

def eth_syncing(url):
    headers = {'content-type': 'application/json'}
    payload = {
        "jsonrpc": "2.0",
        "method": "eth_syncing",
        "params": [],
        "id": 0
    }
    response = requests.post(url, data=json.dumps(payload), headers=headers).json()
    print("--eth_syncing---")
    print(response)
    if response["result"]:
        f=int(response["result"]["startingBlock"],16)
        g=int(response["result"]["knownStates"],16)
        h=int(response["result"]["pulledStates"],16)
        i=int(response["result"]["currentBlock"],16)
        j=int(response["result"]["highestBlock"],16)
        print("startingBlcok:",f)
        print("knownStates:",g)
        print("pulledStates:",h)
        print("currentBlock:",i)
        print("highestBlock:",j)

def eth_getBalance(url,addr):
    headers = {'content-type': 'application/json'}
    payload = {
        "jsonrpc": "2.0",
        "method": "eth_getBalance",
        "params": [addr,"latest"],
        "id": 0
    }
    response = requests.post(url, data=json.dumps(payload), headers=headers).json()
    print("--eth_getBalance---")
    print(response)
    i=int(response["result"],16)
    print("getBalance:",i)

def net_peerCount(url):
    headers = {'content-type': 'application/json'}
    payload = {
        "jsonrpc": "2.0",
            "method": "net_peerCount",
            "params": [],
        "id": 0
    }
    print("--eth_peerCount---")
    response = requests.post(url, data=json.dumps(payload), headers=headers).json()
    print(response)
    i=int(response["result"],16)
    print("peerCount:",i)

def eth_coinbase(url):
    headers = {'content-type': 'application/json'}
    payload = {
        "jsonrpc": "2.0",
        "method": "eth_coinbase",
        "params": [],
        "id": 0
    }
    print("--eth_coinbase---")
    response = requests.post(url, data=json.dumps(payload), headers=headers).json()
    print(response["result"])

def eth_hashrate(url):
    headers = {'content-type': 'application/json'}
    payload = {
        "jsonrpc": "2.0",
        "method": "eth_hashrate",
        "params": [],
        "id": 0
    }
    print("--eth_hashrate---")
    response = requests.post(url, data=json.dumps(payload), headers=headers).json()
    print(response["result"])

def eth_blockNumber(url):
    headers = {'content-type': 'application/json'}
    payload = {
        "jsonrpc": "2.0",
        "method": "eth_blockNumber",
        "params": [],
        "id": 0
    }
    print("--eth_blocknumber---")
    response = requests.post(url, data=json.dumps(payload), headers=headers).json()
    i=int(response["result"],16)
    print(response)
    print("eth_blockNumber:",i)

if __name__ == "__main__":
    url = "http://localhost:8545"
    eth_syncing(url)
    eth_getBalance("xxxxx") 
    net_peerCount(url)
    eth_coinbase(url)
    eth_blockNumber(url)

eth_getBalance("xxxxx") はcoinbaseのアドレスを指定すること。

結果
--eth_syncing---
{'jsonrpc': '2.0', 'id': 0, 'result': False}
--eth_getBalance---
{'jsonrpc': '2.0', 'id': 0, 'result': '0x18fbbca3faa8c76'}
getBalance: 112514993033481334
--eth_getBalance---
{'jsonrpc': '2.0', 'id': 0, 'result': '0x0'}
getBalance: 0
--eth_peerCount---
{'jsonrpc': '2.0', 'id': 0, 'result': '0x6'}
peerCount: 6
--eth_coinbase---
0xxxxxxxxxx
--eth_blocknumber---
{'jsonrpc': '2.0', 'id': 0, 'result': '0x3f56df'}
eth_blockNumber: 4151007

これでEthereumのライブネット環境へpythonからアクセスできる。