複数台Mac実機のDocker SwarmでEthereumプライベートチェーンが動かせない方法


(動かない)構成


  • 各自のPCでSwarmクラスターを構成

  • attachableなOverlayネットワークを1つ作成

  • Ethereumクライアントは公式イメージ(ethereum/client-go)を使用

  • 各自のPCでEthereumコンテナ起動(上記Overlayネットワーク指定)

  • P2P接続する(手動)



プライベートチェーン用ネットワークの作成



※以下のSwarm設定動きません



Swarmの初期化


  • Swarmの初期化をしたホストが最初のmanagerノードになる

  • この時点で指定した2377ポートlistenしてない

docker swarm init --advertise-addr '192.168.x.x'



  • --listen-addr指定したときエラーが出てる時点でもうなんかダメっぽい

docker swarm init --listen-addr '192.168.x.x'

Error response from daemon: manager stopped: failed to listen on remote API address: listen tcp 192.168.x.x:2377: bind: cannot assign requested address


  • ファイヤーウォールはオフ

  • 8545ポートのJSON-RPC呼ぶだけなら通る



Swarmのクラスターに参加



  • workerノードとして参加する

  • トークンはmanagerにもらう

  • ここまででSwarmクラスターは完成(しない)


  • connection refusedで繋がらない

docker swarm join --token SW...5j 192.168.x.x:2377



Overlayネットワークの作成


  • 全コンテナがこのネットワークに参加する

  • コンテナ起動時にこのprivate-chainネットワークを指定する

docker network create -d overlay --attachable private-chain



Ethereumノードを建てる



データディレクトリの作成


  • 各人の環境に合わせて好きな場所に作成

  • ブロックチェーンのDB(data/ethereum/)とマイニング用のDAG(data/ethash/)を保存する

  • コンテナ起動時にフルパスでボリューム指定する

mkdir -p /Users/$USER/data



genesis.jsonの作成


  • ジェネシスブロック(最初のブロック)作成用のファイル

  • Ethereumの初期化時に使用する

  • 上記工程で作ったdataディレクトリ直下にgenesis.jsonというファイル名で配置

{

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



イメージ取得


  • 公式イメージを取得

  • 何も指定せず起動すると、Ethereumメインネット(本番環境)と同期が始まってしまうので、まだ起動しない

docker pull ethereum/client-go



コンテナの起動



  • ENTRYPOINT/bin/shに書き換えて起動


  • -vには上記工程で作成したdataディレクトリを指定

  • ローカルのbridgeネットワーク内で起動する場合--network指定なし

docker run -dit --name eth1 --entrypoint /bin/sh -v /Users/$USER/data:/root/data -p 8545:8545 -p 30303:30303 ethereum/client-go


  • 上記工程で作成したOverlayネットワーク内で起動する場合

docker run -dit --name eth1 --entrypoint /bin/sh --network private-chain -p 8545:8545 -v /Users/$USER/data:/root/data ethereum/client-go



ジェネシスブロックの作成



  • attachでコンテナ名を指定してに接続

  • detachするにはCtrl+pCtrl+q


  • Ctrl-dexitするとコンテナが終了してしまうので注意

docker attach eth1



  • genesis.jsonを指定してジェネシス・ブロックを生成

geth --datadir /root/data/ethereum init /root/data/genesis.json



アカウントの作成


  • ウォレットに最初のアカウントを追加

  • このアカウントにマイニング報酬が送金される

geth --datadir /root/data/ethereum account new

Passphrase:
Repeat passphrase:
Address: {1a...d5}



Ethereumノードの起動



  • gethを起動してREPL環境を表示


  • --miner.etherbaseには上記工程で作成したアカウントアドレスを指定


  • --allow-insecure-unlockを付けないとpersonal.unlockAccount出来なくなってたので付ける

geth --networkid 10101 --datadir /root/data/ethereum --ethash.dagdir /root/data/ethash --miner.etherbase 1a...d5 --nodiscover --allow-insecure-unlock --rpc --rpcaddr '0.0.0.0' console


  • docker起動時にgethを起動する場合は全てのオプションを1行にまとめてコンテナ起動してもいい

docker run -dit --name eth -v /Users/$USER/data:/root/data -p 8545:8545 -p 30303:30303 ethereum/client-go --networkid 10101 --datadir /root/data/ethereum --ethash.dagdir /root/data/ethash --miner.etherbase 1a...d5 --nodiscover --allow-insecure-unlock --rpc --rpcaddr '0.0.0.0' console

↓上記コマンドを見やすく整形したもの

docker run  -dit \

--name eth \
-v /Users/$USER/data:/root/data \
-p 8545:8545 \
-p 30303:30303 \
ethereum/client-go --networkid 10101 \
--datadir /root/data/ethereum \
--ethash.dagdir /root/data/ethash \
--miner.etherbase 1a...d5 \
--nodiscover \
--allow-insecure-unlock \
--rpc --rpcaddr '0.0.0.0' \
console


  • Ethereumで使われているポート


    • 8545: JSON-RPC

    • 8546: WebSocket

    • 8547: GraphQL

    • 30303/tcp: P2P用

    • 30303/udp: P2Pオートディスカバリ用





EthereumのJSコンソール



web3.js


  • web3.js v0.x系のAPIが使える

> eth.blockNumber

0



P2Pネットワークの形成



他ノードへの接続



  • --nodiscoverフラグを指定してるので手動で接続する必要がある

  • 接続していないことを確認

> admin.peers

[]
> net.peerCount
0


  • consoleに入ったらenode情報を取得

> admin.nodeInfo.enode

"enode://15...1e@127.0.0.1:30303?discport=0"



  • enode内のIPアドレスをコンテナのIPアドレスに変更して接続

> admin.addPeer('enode://15...1e@10.x.x.x:30303?discport=0')

true


  • やっぱり接続していないことを確認

  • ピア情報がでてくる予定だった

> admin.peers

[]
> net.peerCount
0



操作



マイニング


  • 初回はDAGの生成に5分ほどかかる

> eth.mining

false
> miner.start(1)
INFO [04-03|15:41:46.204] Updated mining threads threads=1
INFO [04-03|15:41:46.204] Transaction pool price threshold updated price=1000000000
INFO [04-03|15:41:46.205] Commit new mining work number=1 sealhash=caeddc34d5e1 uncles=0 txs=0 gas=0 fees=0 elapsed=322.5µs
INFO [04-03|15:41:50.540] Generating DAG in progress epoch=0 percentage=0 elapsed=3.556s
INFO [04-03|15:41:54.010] Generating DAG in progress epoch=0 percentage=1 elapsed=7.025s
INFO [04-03|15:41:57.472] Generating DAG in progress epoch=0 percentage=2 elapsed=10.488s
:
INFO [04-03|15:47:38.808] Generating DAG in progress epoch=0 percentage=98 elapsed=5m52.229s
INFO [04-03|15:47:42.314] Generating DAG in progress epoch=0 percentage=99 elapsed=5m55.735s
INFO [04-03|15:47:42.326] Generated ethash verification cache epoch=0 elapsed=5m55.747s
INFO [04-03|15:47:47.763] Successfully sealed new block number=1 sealhash=caeddc34d5e1 hash=49ceef74d8f4 elapsed=6m1.963s
INFO [04-03|15:47:47.765] 🔨 mined potential block number=1 hash=49ceef74d8f4
> eth.mining
true


  • マイナーを止めてマイニング停止

  • DAGの生成が止まらないが、いずれそれも止まるので動揺しない

> miner.stop()



アカウントの確認

> eth.accounts

["0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8"]
> eth.accounts[0]
"0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8"



残高の確認


  • 100京wei = 1ether

> eth.getBalance(eth.accounts[0])

15
> web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')
15



アカウントのアンロック


  • 第3引数を0にするとプロセス起動中ずっとアンロック


  • --allow-insecure-unlockを付けないとエラーになるので注意

> personal.unlockAccount(eth.accounts[0],null,0)

Unlock account 0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8
Passphrase:
true



トランザクションの送信


  • いったんマイナーを止めとく

  • トランザクションの送信には手数料(ETH)が必要

> bn = eth.blockNumber

13
> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[0],value:web3.toWei('1','ether')})
"0x0a...87"



トランザクションプール

> txpool.status

{
pending: 1,
queued: 0
}
> txpool.content
{
pending: {
0x12AF8FCE5685C5e6F25DA9dd8eF4Da100CC9c0B8: {
2: {
blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
blockNumber: null,
from: "0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8",
gas: "0x15f90",
gasPrice: "0x3b9aca00",
hash: "0x52bea06df31909b95fd201c098d4d4cc727de5bda9595370edfa9966a76d3d70",
input: "0x",
nonce: "0x2",
r: "0xc0dc7663149fba52e9a4ab357c8b37cfb051794187dfc9cd1f021b318077fa5a",
s: "0x441004d231de0e425bdfdbe961a07fdcedce38b347f3b79809394299f2988b6",
to: "0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8",
transactionIndex: "0x0",
v: "0x4f0e",
value: "0xde0b6b3a7640000"
}
}
},
queued: {}
}
> txpool.inspect
{
pending: {
0x12AF8FCE5685C5e6F25DA9dd8eF4Da100CC9c0B8: {
2: "0x12AF8FCE5685C5e6F25DA9dd8eF4Da100CC9c0B8: 1000000000000000000 wei + 90000 gas × 1000000000 wei"
}
},
queued: {}
}



ブロックの確認


  • マイニングを開始すると上記保留されてたトランザクションがブロックに取り込まれる


  • txpoolを確認すると空になってるはず

> eth.getBlock(bn+1)

{
difficulty: 131072,
extraData: "0xd8...78",
gasLimit: 3181688,
gasUsed: 21000,
hash: "0x5e...c3",
logsBloom: "0x00...00",
miner: "0x12...b8",
mixHash: "0x8a7c653952bf27288666a31263923d037dda944f4ce41260a4474e0df0d54e6b",
nonce: "0x3bdf8017b06618ae",
number: 13,
parentHash: "0x0e31202579fa2c97bb9546e261bf037821bd9c75a935e1a64c0b648b959e08ab",
receiptsRoot: "0x4287c2ed8bce2ced130f5c319d2cde9bc0fceefc273de7406b7112c68175d049",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 651,
stateRoot: "0x2eb370e6cc431ede290354bcb0b0abec74f16bacb2903703459c0f3e02d9a675",
timestamp: 1554883148,
totalDifficulty: 1838016,
transactions: ["0x170250400e5f087b90a235023f2460e9031bac12ad2683663fea2111711fb5a2"],
transactionsRoot: "0x2df21628faf3e362cd5bec2c90ad6a5d0bca59c3b75bc2e2f51a832806b4d09b",
uncles: []
}



トランザクションの確認

> eth.getTransaction(eth.getBlock(bn+1).transactions[0])

{
blockHash: "0x5ec8f2c51e7e2460a7def8cca57dbbf833d39f651f4b40168c9a4b92a149c5c3",
blockNumber: 13,
from: "0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8",
gas: 90000,
gasPrice: 1000000000,
hash: "0x170250400e5f087b90a235023f2460e9031bac12ad2683663fea2111711fb5a2",
input: "0x",
nonce: 1,
r: "0xffd2eb20323e2fa0576fcad9c9fd3a5650f8e388e40d9b56a2e0765c1fc31f41",
s: "0x792e01ad9da9663f020c679db8c8e8afbd04d6e4260687b64d1f9e7519cd3d4c",
to: "0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8",
transactionIndex: 0,
v: "0x4f0d",
value: 1000000000000000000
}