It’s now or never

IT系の技術ブログです。気になったこと、勉強したことを備忘録的にまとめて行きます。

【Ethereum】EthereumでPrivateNetworkを作ってみる

仕事の関係でブロックチェーンのプラットフォームであるEthereumを調べることになり、
簡単な動作確認を行うためにプライベートのEthereumのネットワークを作成してみました。

  • ※ 調べながらのものなのでところどころ間違っているかもしれません。
  • ブロックチェーン、仮想コイン自体の知識も深くないためそちらも認識に齟齬があるかもしれません。

動作環境

OS: ubuntu:14.04 * ※ 僕の環境はMacOSなのですが、試行錯誤なので環境がよごれないようにDockerでubuntuをインストールして試しています。 * ※ Docker周辺の操作自体は割愛させていただきます。

Ethereumとは

Ethereumは、ブロックチェーンを使ったアプリケーションのプラットフォームです。 (Ethereumブロックチェーンの技術詳細については、ここでは割愛します。) ブロックチェーンを使ったアプリケーションを作成する方法は大きく分けて2つあります。
1つは、「ゼロからブロックチェーンのネットワークを構築する方法」、もう一つは「既存のブロックチェーンの技術基盤を利用して構築する方法」です。
Ethereumは後者にあたるもので、Ethereumを利用することでブロックチェーンを利用したアプリケーションを低コストで作成することができます。

Gethをインストールする

EthereumのP2Pネットワークに参加するためにGethというクライアントツールが提供されています。 (GethはGo言語製のツールです) Gethをインストールすることで、

といった操作を行うことができます。 まずは、このツールをインストールします。

sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo add-apt-repository -y ppa:ethereum/ethereum-dev
sudo apt-get update
sudo apt-get install ethereum

ネットワーク用のディレクトリを作る

mkdir eth_private
cd eth_private

※ このテスト実装では、/ethereumというディレクトリをルートディレクトリとして作業しています。

アカウントをつくる

一番初めのアカウントを定義します。

geth --datadir /ethereum/eth_private account new
WARN [07-09|03:57:57] No etherbase set and no accounts found as default
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
  • No etherbase set and no accounts found as default

    • この警告はなんでしょう?デフォルトのアカウントがないよという意味のようですが、何もない状態でアカウントを作成しているから?一旦無視します。
  • パスワードを聞かれるため入力する

    • ※ 注意 パスワードを忘れるとあとで確認する方法がないため、忘れない方法で保管しておきます。
Address: {4e2e0ef96802a54c8e1d5c937e38290c42ced5f0}
  • 発行されるとアカウントの公開キーが表示されます。(アカウントのアドレスと呼ばれるものです。)
  • アカウントの作成が成功すると作業ディレクトリ配下にkeystoreというディレクトリが作成されています。
  • さらにその配下にはUTC-xxxから始まるjsonファイルが作成されています。
root@03be5503f430:/ethereum/eth_private# ls -la
total 0
drwxr-xr-x 3 root root 102 Jul  9 03:58 ./
drwxr-xr-x 5 root root 170 Jul  9 03:57 ../
drwx------ 3 root root 102 Jul  9  2017 keystore/
root@03be5503f430:/ethereum/eth_private# ls -la keystore/
total 4
drwx------ 3 root root 102 Jul  9 04:04 ./
drwxr-xr-x 3 root root 102 Jul  9 03:58 ../
-rw------- 1 root root 491 Jul  9 03:58 UTC--2017-07-09T03-58-15.183698355Z--4e2e0ef96802a54c8e1d5c937e38290c42ced5f0
{
    "address": "4e2e0ef96802a54c8e1d5c937e38290c42ced5f0",
    "crypto": {
        "cipher": "aes-128-ctr",
        "ciphertext": "ee886a6635bc095337d643de774ec86c38c32016a1f56c5f3d1e9fb092993193",
        "cipherparams": {
            "iv": "caa069b4c621db2ddd3c9aa11acd9de2"
        },
        "kdf": "scrypt",
        "kdfparams": {
            "dklen": 32,
            "n": 262144,
            "p": 1,
            "r": 8,
            "salt": "8453090efc0f98075afbb046521273909a3c26d6244e2453b83ddae7d6d0540c"
        },
        "mac": "85249864f73dd75ed70e9e8e2123aa0d10d2c5f73d7bfde510fa547804d092d3"
    },
    "id": "a9d3805d-0a8a-4ef4-a89a-1c0949737951",
    "version": 3
}
  • addressは先程作成したアカウントの公開キーと一致するためこれがアカウント情報のようです。(詳細はここでは追いません)

GenesisBlockの設定ファイルを作成する

  • 一番はじめのブロックを定義したファイルをGenesisファイルといいます。
  • ブロックチェーンのネットワークを作成するために、このGenesisファイルを初めに作成します。
{
    "config": {
        "chainId": 13,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
    "difficulty": "200000000",
    "gasLimit": "2100000",
    "alloc": {
        "4e2e0ef96802a54c8e1d5c937e38290c42ced5f0": { "balance": "100000000" }
    }
}
  • chainId: 後に指定するネットワークIDと合わせたID
  • homesteadBlock: 0ブロック以降がHomesteadであるという設定
    • homesteadとは、Ethereumのバージョンのことでこのバージョンの前後で処理が異なるみたいです
  • eip155Block: 不明
  • eip158Block: 不明
  • difficulty: マイニングの難易度。低ければ低い程、短時間でマイニングできるようです。
  • gasLimit: トランザクションで指定できる最大gas
  • alloc: 初期Etherの設定。ここで先程作成したアカウントを指定しています。
    • 単位はwei

GenesisBlockを初期化する

geth --datadir /ethereum/eth_private  init /ethereum/eth_private/CustomGenesis.json
  • ここでもネットワークのディレクトリを指定してます。
INFO [07-09|04:15:31] Allocated cache and file handles         database=/ethereum/eth_private/geth/chaindata cache=16 handles=16
INFO [07-09|04:15:31] Writing custom genesis block
INFO [07-09|04:15:31] Successfully wrote genesis state         database=chaindata                            hash=6aa7ce…33ec9a
INFO [07-09|04:15:31] Allocated cache and file handles         database=/ethereum/eth_private/geth/lightchaindata cache=16 handles=16
INFO [07-09|04:15:31] Writing custom genesis block
INFO [07-09|04:15:31] Successfully wrote genesis state         database=lightchaindata                            hash=6aa7ce…33ec9a
  • 上記のように表示されれば正しく初期化がされています。
root@03be5503f430:/ethereum/eth_private# ls -la
total 4
drwxr-xr-x 5 root root 170 Jul  9 04:15 ./
drwxr-xr-x 5 root root 170 Jul  9 03:57 ../
-rw-r--r-- 1 root root 288 Jul  9 04:14 CustomGenesis.json
drwxr-xr-x 4 root root 136 Jul  9 04:15 geth/
drwx------ 3 root root 102 Jul  9 04:04 keystore/
root@03be5503f430:/ethereum/eth_private# ll geth/
total 0
drwxr-xr-x 4 root root 136 Jul  9 04:15 ./
drwxr-xr-x 5 root root 170 Jul  9 04:15 ../
drwxr-xr-x 7 root root 238 Jul  9 04:15 chaindata/
drwxr-xr-x 7 root root 238 Jul  9 04:15 lightchaindata/
  • geth配下にはchaindataというディレクトリがあり、ここにはブロックチェーンの起源を表すleveldbデータベースファイルと、それが採掘されてブロックチェーンに追加された後のブロックが含まれているようです。
  • つまり新しいブロックが作られるとこのDBにデータが追加されていくようです。

Gethを起動(コンソール)

  • Gethを起動しネットワークにattachします。
  • Gethには対話式のコンソールが用意されています。このコンソールを起動して色々確認してみます。
geth --networkid 13 --datadir /ethereum/eth_private console 2>> /ethereum/eth_private/geth_err.log
instance: Geth/v1.6.6-stable-10a45cb5/linux-amd64/go1.8.1
coinbase: 0x4e2e0ef96802a54c8e1d5c937e38290c42ced5f0
at block: 0 (Thu, 01 Jan 1970 00:00:00 UTC)
 datadir: /ethereum/eth_private
 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
 
 >
  • networkidには、Genesisファイルで定義したchainIdを指定します。
  • errer出力をlogファイルに書き出しています。

アカウントの確認

> eth.accounts
["0x4e2e0ef96802a54c8e1d5c937e38290c42ced5f0"]
  • 先程作成したアカウントが確認できます。

etherbase(coinbase)の確認

> eth.coinbase
"0x4e2e0ef96802a54c8e1d5c937e38290c42ced5f0"
  • etherbase(coinbase)とは、そのノードで発掘を行った時にその報酬を紐付けるEOAのアドレスを指します。

所持Etherの確認

> eth.getBalance("0x4e2e0ef96802a54c8e1d5c937e38290c42ced5f0")
100000000
  • 引数にアカウントのaddressを指定します。
  • Genesisファイルで初めに指定したEther(単位はwei)を所持していることが確認できます

Gethを起動(JSON-RPC)

  • コンソールを使わずにネットワークにattachします。
geth \
 --datadir /ethereum/eth_private \
 --mine \
 --nodiscover \
 --maxpeers 0 \
 --networkid 13 \
 --rpc \
 --rpccorsdomain "*"
  • mine: マイニングを許可する。このオプションを指定するとマイニングが始まるようです。
  • nodiscover: Gethはデフォルトで自動的に(同じネットワークID)のEthereumネットワークのノード(Peer)を探し接続を試みます。プライベート・ネットで未知のノードとの接続を避けるため、このオプションを指定することで自動Peer探索機能を無効にします。
  • maxpeers ネットワークに接続するノードの最大数(デフォルト25) 0を指定するとネットワークが無効になります。
  • networkid ネットワークID(デフォルト1) 1を指定するとグローバルのネットワークに接続されます。
    • 2,3,4も予約されているようです。(2=Morden (disused), 3=Ropsten)
  • rpc HTTP-RPC serverとして動作を許可する
  • rpccorsdomain クロスオリジンを許可するリスト
root@03be5503f430:/ethereum/eth_private# geth --datadir /ethereum/eth_private  --mine  --nodiscover  --maxpeers 0  --networkid 13  --rpc  --rpccorsdomain "*"
INFO [07-09|06:02:23] Starting peer-to-peer node               instance=Geth/v1.6.6-stable-10a45cb5/linux-amd64/go1.8.1
INFO [07-09|06:02:23] Allocated cache and file handles         database=/ethereum/eth_private/geth/chaindata cache=128 handles=1024
INFO [07-09|06:02:23] Initialised chain configuration          config="{ChainID: 13 Homestead: 0 DAO: <nil> DAOSupport: false EIP150: <nil> EIP155: 0 EIP158: 0 Metropolis: <nil> Engine: unknown}"
INFO [07-09|06:02:23] Disk storage enabled for ethash caches   dir=/ethereum/eth_private/geth/ethash count=3
INFO [07-09|06:02:23] Disk storage enabled for ethash DAGs     dir=/root/.ethash                     count=2
INFO [07-09|06:02:23] Initialising Ethereum protocol           versions="[63 62]" network=13
INFO [07-09|06:02:23] Loaded most recent local header          number=0 hash=6aa7ce…33ec9a td=200000000
INFO [07-09|06:02:23] Loaded most recent local full block      number=0 hash=6aa7ce…33ec9a td=200000000
INFO [07-09|06:02:23] Loaded most recent local fast block      number=0 hash=6aa7ce…33ec9a td=200000000
INFO [07-09|06:02:23] Starting P2P networking
INFO [07-09|06:02:23] RLPx listener up                         self="enode://9df46f04a32634b47ce6fe8203b8077029550890d5f118b6ad88aaee15db9d96ac819f119a3997c5b5c9cecff395b866c7c45c39f830ce3111d63fb7c5853bed@[::]:30303?discport=0"
INFO [07-09|06:02:23] IPC endpoint opened: /ethereum/eth_private/geth.ipc
INFO [07-09|06:02:23] HTTP endpoint opened: http://127.0.0.1:8545
INFO [07-09|06:02:23] Transaction pool price threshold updated price=18000000000
INFO [07-09|06:02:23] Starting mining operation
INFO [07-09|06:02:23] Commit new mining work                   number=1 txs=0 uncles=0 elapsed=178.695µs
INFO [07-09|06:02:28] Generating DAG in progress               epoch=0 percentage=0 elapsed=3.764s
INFO [07-09|06:02:30] Generating DAG in progress               epoch=0 percentage=1 elapsed=6.657s
  • gethを起動するとしばらく Generating DAG in progressというログが流れる。これはDAGという数Gバイトのデータセットを作成しているログのようです。
  • これはEthashというProof of Workを行うための仕組みで使われるもの。詳しくはここを参照。
  • DAGはデフォルトで$(HOME)/.ethash配下に作成されています。
INFO [07-09|07:09:58] Successfully sealed new block            number=1 hash=ef4ec0…62e718
INFO [07-09|07:09:58] 🔨 mined potential block                  number=1 hash=ef4ec0…62e718
INFO [07-09|07:09:58] Commit new mining work                   number=2 txs=0 uncles=0 elapsed=431.601µs
  • しばらくすると(1,2時間くらい)マイニングが始まりブロックが作成されます

JSON-RPCで通信してみる

  • JSON-RPCで対応しているメソッドはここに定義されています。
クライアントのバージョン(web3_clientVersion)
curl -X POST --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}' http://localhost:8545
{"jsonrpc":"2.0","id":1,"result":"Geth/v1.6.6-stable-10a45cb5/linux-amd64/go1.8.1"
Etherの確認(eth_getBalance)
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x4e2e0ef96802a54c8e1d5c937e38290c42ced5f0", "latest"],"id":1}' http://localhost:8545
{"jsonrpc":"2.0","id":1,"result":"0x5f5e100"}
  • resultには16進数の値が入っている。これを10進数に変換すると100000000となるので正しい値が入っていることがわかります。

参考

Private-network How to Build a Private Ethereum Blockchain