Ethereumのプライベートネットワークを作って採掘ごっこしてみる(Geth on macOS)
About
分散アプリケーションプラットフォーム「Ethereum(イーサリアム)」について学習中の筆者が、
①GethというCUIクライアントをインストールして、
②自分のプライベートネットワークで自分のブロックチェーンを作って、
③採掘してみる
という内容です。
オープンソースのドキュメント「Ethereum入門」のチュートリアルの一部を参考にしています。
この記事で作ったブロックチェーンのデータディレクトリはGitHubで公開しています。cloneして使えるかは試していませんが、参考までに。
https://github.com/kecbigmt/eth-private-demo
注意点
この記事で説明しているのは、あくまでプライベートネットワーク上での実験です。パブリックの取引や採掘には一切関与しないため、採掘して得たetherをパブリックのアカウントに送金することはできないし、ご自身のパブリックのether残高にアクセスすることもありません。
この記事がバイブルにしている「Ethereum入門」ではプライベートネットワークについて以下のように言及しています。
ここでプライベート・ネットワークは、自分自身のみのネットワークなので容易にEtherの採掘が可能ですし、安全性も高いネットワークです。そのため、Ethereumの動作を調べたり、分散型アプリケーション(Dapp)の開発作業など個人的な作業を行うには、プライベート・ネットワークを立ち上げてそこでいろいろ弄ってみると便利です。
用意する環境・知識
前置きはこれくらいにとどめて、ここからが本題です。
環境
- macOS 10.13.4
- Homebrew 1.6.2
必要な知識
- ブロックチェーンに対するざっくりとした理解
- Terminalの基本的な操作
- 基本的なUNIXコマンド
流れ
- Gethのインストール
- ブロックチェーンを創世する
- Gethを起動
- アカウントを作成する
- 採掘してみる
- 採掘されたブロックを確認する
1. Gethのインストール
まず、Terminalを起動して、HomebrewでGethをインストールします。
$ brew tap ethereum/ethereum
$ brew install ethereum
インストールできたら、geth --help
で動作確認しましょう。正しくインストールされていれば、CopyrightやVersionなどがバーっと出てきます。
$ geth --help
NAME:
geth - the go-ethereum command line interface
Copyright 2013-2017 The go-ethereum Authors
USAGE:
geth [options] command [command options] [arguments...]
VERSION:
1.8.4-stable
COMMANDS:
account Manage accounts
attach Start an interactive JavaScript environment (connect to node)
bug opens a window to report a bug on the geth repo
# 以下省略
2. ブロックチェーンを創世する
適当な場所で、適当なディレクトリを作ってその配下に移動します。
このディレクトリはデータディレクトリと呼ばれ、このなかにブロックやノードに関する情報が記録されていきます。
続いて、vimでも外部エディタでもなんでもいいんですが、myGenesis.json
というのを作ります。
直訳すれば私の創世記.json
。なんと神々しいJSONファイルでしょうか。
$ mkdir eth-private-demo
$ cd eth-private-demo
$ vim myGenesis.json
myGenesis.json
に書き込むのは以下の内容。これが、このプライベートネットワークにおけるブロックチェーンの、第0号(の情報を示すファイル)になります。アダムとイヴ的なワクワク感がありますね。
{
"config": {
"chainId": 15
},
"nonce": "0x0000000000000042",
"timestamp": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "",
"gasLimit": "0x8000000",
"difficulty": "0x4000",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x3333333333333333333333333333333333333333",
"alloc": {}
}
お次に、geth --datadir ./ init ./myGenesis.json
でブロックチェーンを初期化します。これでmyGenesis.json
を祖とするブロックチェーンが出来上がりました。
$ geth --datadir ./ init ./myGenesis.json
INFO [04-28|03:05:34] Maximum peer count ETH=25 LES=0 total=25
INFO [04-28|03:05:34] Allocated cache and file handles database=/Users/kecy/dev/src/github.com/kecbigmt/eth-private-demo/geth/chaindata cache=16 handles=16
INFO [04-28|03:05:34] Writing custom genesis block
INFO [04-28|03:05:34] Persisted trie from memory database nodes=0 size=0.00B time=9.5µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [04-28|03:05:34] Successfully wrote genesis state database=chaindata hash=7b2e8b…7e0432
INFO [04-28|03:05:34] Allocated cache and file handles database=/Users/kecy/dev/src/github.com/kecbigmt/eth-private-demo/geth/lightchaindata cache=16 handles=16
INFO [04-28|03:05:34] Writing custom genesis block
INFO [04-28|03:05:34] Persisted trie from memory database nodes=0 size=0.00B time=1.971µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [04-28|03:05:34] Successfully wrote genesis state database=lightchaindata hash=7b2e8b…7e0432
これによって、--datedir
オプションで指定したディレクトリのなかに、ブロックチェーンを記録するためのディレクトリが作られます。現時点でのディレクトリ構成はおそらく以下のようになっていると思います。
eth-private-demo/
├ geth/
│ ├ chaindata/
│ │ ├ 000001.log
│ │ ├ CURRENT
│ │ ├ LOCK
│ │ ├ LOG
│ │ └ MANIFEST-000000
│ │
│ └ lightchaindata/
│ ├ 000001.log
│ ├ CURRENT
│ ├ LOCK
│ ├ LOG
│ └ MANIFEST-000000
│
├ keystore/
└ myGenesis.json
3. Gethを起動
プライベートネットワーク上にブロックチェーンを準備できたので、いよいよGethを起動してプライベートネットワークに接続してみましょう。
geth --networkid "15" --nodiscover --datadir "./" console 2>> ./geth_err.log
を実行します。何やら長ったらしいコマンドですが、以下のような意味があるようです。
-
--networkid "15"
:myGenesis.json
で指定したchainId
と同一の値を渡す -
--datadir "./"
: ブロックチェーンのデータやログの出力先を指定する -
--nodiscover
: 見ず知らずのノードに接続しないようにする -
console
: Gethの起動と同時に、対話型のコンソールを立ち上げる。このコンソールからクライアントとしていろんな操作ができる
$ geth --networkid "15" --nodiscover --datadir "./" console 2>> ./geth_err.log
Welcome to the Geth JavaScript console!
instance: Geth/v1.8.4-stable/darwin-amd64/go1.10.1
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
>
おお、>
がプロンプト記号になっているJavaScriptコンソールが現れました。
できたてほやほやのブロックチェーンには、まだ0番目のブロックしか存在しませんが、とりあえずeth.getBlock(0)
で中身を見てみましょう。
> eth.getBlock(0) // 0番目のブロックを確認
{
difficulty: 16384,
extraData: "0x",
gasLimit: 134217728,
gasUsed: 0,
hash: "0x7b2e8be699df0d329cc74a99271ff7720e2875cd2c4dd0b419ec60d1fe7e0432",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x3333333333333333333333333333333333333333",
mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
nonce: "0x0000000000000042",
number: 0,
parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 507,
stateRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
timestamp: 0,
totalDifficulty: 16384,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: []
}
さっき見かけたような感じのする内容が出てきました。そう、myGenesis.json
で指定した内容です。16進数が一部10進数に変換されてたりと差異はありますが、まぎれもなく0番目です。number: 0
ってあるし。
ほかのコマンドも実行してみましょう。eth.accounts
とか。このノードに登録されているアカウントが表示されます。
> eth.accounts // このノードのアカウントを全て取得
[]
まだ何も作っていないので当然すっからかんです。
4. アカウントを作成する
では、アカウントを作ってみましょう。これがないと採掘も送金もできません。
コマンドは、personal.newAccount("パスワード")
。あとで送金するために、2つアカウントを作っておきましょう。
> personal.newAccount("foo") // 1番目のアカウントfooを作成
"0xfb6ab33c38a483e20554d9a4eabb5a6f3886fb2c"
> personal.newAccount("bar") // 2番目のアカウントbarを作成
"0x0efc6673f35b9b9489f539ea965609eb5ae587d0"
出てくるハッシュ文字列は、そのアカウントのアドレス。パスワードと一緒に控えておきましょう。
ちなみにパスワードを忘れると復元することはできないそうです。今回はお試しのプライベートネットワークなので適当に管理して大丈夫ですが、そうじゃない場合はしっかり記録しておいてくださいね。
この状態でもう一度eth.accounts
を実行すると、いま作ったアカウントがきちんと反映されていることがわかります。
> eth.accounts // もう一度アカウントを全て取得
["0xfb6ab33c38a483e20554d9a4eabb5a6f3886fb2c", "0x0efc6673f35b9b9489f539ea965609eb5ae587d0"]
5. 採掘してみる
さて、いよいよみんな大好き採掘のお時間です。しかしその前に、採掘の報酬がどのアドレスに入るのかを確認しましょう。
各ノードで採掘したときに報酬を受け取れるアドレスは1つだけ。その受取先を確認するには、eth.coinbase
を実行します。
> eth.coinbase // 採掘の報酬受取先アドレスを確認
"0xfb6ab33c38a483e20554d9a4eabb5a6f3886fb2c"
パスワードがfoo
のほうですね。eth.accounts
の1番目です。どうせなのでbar
に切り替えてみましょう。miner.setEtherbase(eth.accounts[1])
で、2番目のbar
に切り替わります。
> miner.setEtherbase(eth.accounts[1]) // 2番目のアカウントbarを報酬受取先に指定
true
これで準備完了。miner.start()
で採掘を開始します。この採掘の報酬は、bar
のアドレスに入ってきます。
null
が返ってくれば始まっているはず。
> miner.start() // 採掘スタート!
null
不安であればeth.mining
で採掘中かどうかを確認できます。また、いま何番目までのブロックまで採掘されたかを確認するにはeth.blockNumber
です。
> eth.mining // 採掘中かどうかを確認
true
> eth.blockNumber // ブロックチェーンのブロック数を確認
0
ただし、初回の採掘では、DAGと呼ばれるデータセットを生成する作業が行われるため、実際の採掘までは10〜30分ほどかかります。気長に待ちましょう。
このDAGは、Ethereumの採掘や検証で必要になります。ファイルサイズが1GBほどあり、macOSであれば$HOME/.ethash/
以下に格納されます。
※DAGの詳細はWhite Paperを参照
https://github.com/ethereum/wiki/wiki/Ethash
eth.blockNumber
を実行して1以上の数字が返ってきたら実際の採掘が始まっています。適当なところでminer.stop()
を打ち込めば採掘を停止できます。
> eth.blockNumber // ブロックチェーンのブロック数を確認
4960
> miner.stop() // 採掘を停止
true
↑採掘初めて早々寝落ちして、起きてみたら結構な数のブロックが採掘されてた図。
6. 採掘されたブロックを確認する
採掘されたブロックをeth.getBlock()
で確認してみましょう。
> eth.getBlock(100) // 100番目のブロックを確認
{
difficulty: 137313,
extraData: "0xd983010804846765746888676f312e31302e318664617277696e",
gasLimit: 121724529,
gasUsed: 0,
hash: "0x7c06d551273c98746a5e4b0682b72de5fda367b36526c78d052c61b13762f7bc",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x0efc6673f35b9b9489f539ea965609eb5ae587d0",
mixHash: "0x59929902c691655868aa59cf53bfd2be9c40e8bf569ba767f94c675f7c5ea20d",
nonce: "0x01ae8f238386d18c",
number: 100,
parentHash: "0x6744542fb19458390d875119a9a271751c4aeedabb5a4d320587148faf4acc53", // このブロックの親(99)のhash
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 538,
stateRoot: "0x6de62e0a0e88979e9f04b927141aa28b2cd804d27237aca5e935f246d4801603",
timestamp: 1524858921,
totalDifficulty: 13424369,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: []
}
細かいフィールドの説明は省きますが、miner
にはこのブロックを採掘したアカウントbar
のアドレスが入っていて、parentHash
にはこのブロックの前のブロック、つまり99番目のブロックのhash値が入っています。
99番目のブロックのhash
を確認すれば、100番目のparentHash
と一致していることがわかります。
> eth.getBlock(99) // 99番目のブロックを一部抜粋
{
hash: "0x6744542fb19458390d875119a9a271751c4aeedabb5a4d320587148faf4acc53", // このブロックの子(100)のparentHashと一致
nonce: "0x73169a1acb975eb4",
number: 99,
parentHash: "0x8898af4a904dea9591f5de39d412d772dd3b08ea11a17b851cd5e15516b7e685",
...
}
本当にブロックチェーンって連なってるんだなあって実感できます。
7. 採掘の報酬を確認する
eth.getBalance()
で、アカウントの残高を確認できます。今回採掘の報酬受取先にしていたのは2番目のアカウントbar
なので、eth.accounts[1]
に報酬が入っていることがわかります。
> eth.getBalance(eth.accounts[0]) // 1番目のアカウントfooの残高を確認
0
> eth.getBalance(eth.accounts[1]) // 2番目のアカウントbarの残高を確認
2.481e+22
ただし、これはether単位ではなく、weiという単位での数字。1 ether = 1,000,000,000,000,000,000 weiなので、とんでもない数字になっています。
web3.fromWei()
を使って、weiを他の単位に換算できます。ここではetherに換算してみましょう。
> web3.fromWei(eth.getBalance(eth.accounts[0]),"ether") // 1番目のアカウントfooの残高をether単位で確認
0
> web3.fromWei(eth.getBalance(eth.accounts[1]),"ether") // 2番目のアカウントbarの残高をether単位で確認
24810
まとめ
ここまでで、以下のことを見てきました。
- EthereumのクライアントGethをインストール
- Gethでプライベートネットワークをに接続してブロックチェーンを新しく作る
- アカウントを作成して、採掘して、その結果を確認してみる
Gethという優秀なクライアントソフトが用意されているので、Ethereumに参加するだけであれば、プログラミング知識はほとんど必要ないことがわかります。
プログラミングやブロックチェーンの初心者には、とりあえずGethを使ってしばらく遊んでみることをおすすめします。プライベートネットワーク上で遊んでいる限りは何をしても実害を被ることはないはずです。
私もブロックチェーン学習中の身なので、これからいろいろ読んでみていじってみて、どんどん投稿していきます。
次回の記事はこちら
Ethereumのプライベートネットワークで送金してみる(Geth on macOS)
References
- Ethereum入門: https://book.ethereum-jp.net/
- ethereum/go-ethereum
- Repository: https://github.com/ethereum/go-ethereum
- Installation Instructions for Mac: https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-Mac
- ethereum/wiki
- Repository: https://github.com/ethereum/wiki
- Ethash: https://github.com/ethereum/wiki/wiki/Ethash