⛓️

Dockerを使ってGethをプライベートネットワークで実行してみる

2022/05/03に公開

Gethとはイーサリアムクライアントの一つです。

ブロックチェーンアプリケーション開発の教科書(リフロー版)
https://www.amazon.co.jp/dp/B079JYHZY3/
を参考に、Dockerを使ってGethをプライベートネットワークで実行し、マイニングをしてみます。
書籍ではWindowsとmacOSを使った方法しかないのでDockerでの利用方法と、
書籍に書かれている情報がいくつか古いものがあるので現時点での動かし方について書いていきます。

docker-composeでGethを利用できるようにする

docker runでも動きますがオプションの指定を楽にしたかったのでdokcer-composeを使っています。
イメージはethereum/client-goというのがあるのでこれを使います。
https://hub.docker.com/r/ethereum/client-go/tags

version: "3"
services:
  app:
    image: ethereum/client-go
    entrypoint: "/bin/sh"
    tty: true
    volumes:
      - .:/geth

entrypoint: "/bin/sh"
について。

ethereum/client-goのentrypointが
ENTRYPOINT ["geth"]
となっている。
https://hub.docker.com/layers/client-go/ethereum/client-go/latest/images/sha256-f1d3acc0c2718db610baec236b77a7095dd395c333a979200a9d14ca2e93e3e3?context=explore

そのためそのままdocker-compose upするとgethのネットワークが起動してしまう。
一旦コンテナ内で自由にgethコマンドを使えるようにしたいので、
entrypoint: "/bin/sh"
としてdocker-compose upでネットワークが起動しないようにしている。

起動

mbazuki:geth-sandbox hide$ docker-compose up
Recreating geth-sandbox_app_1 ... done
Attaching to geth-sandbox_app_1

コンテナの中に入る

mbazuki:geth-sandbox hide$ docker-compose exec app /bin/sh
/ # ls
bin    etc    home   media  opt    root   sbin   sys    usr
dev    geth   lib    mnt    proc   run    srv    tmp    var
/ # 

/ # geth version
Geth
Version: 1.10.18-unstable
Git Commit: f94e23ca66eef8fdac2473ce99ca6ad57324aaa2
Architecture: amd64
Go Version: go1.18.1
Operating System: linux
GOPATH=
GOROOT=go

これでDockerコンテナ内でgethコマンドが使えるようになりました。

Gethの初期化

プライベートネットワークはブロックが一つもない状態です。
そのため最初のブロック(ジェネシスブロック)を作成し初期化する必要があります。

プライベートネットワーク用のディレクトリprivate_netを作成しその中にgenesis.jsonを作成します。

{
  "config": {
    "chainId": 22,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "istanbulBlock": 0,
    "berlinBlock": 0,
    "londonBlock": 0
  },
  "alloc": {},
  "coinbase": "0x0000000000000000000000000000000000000000",
  "difficulty": "0x20000",
  "extraData": "",
  "gasLimit": "0x2fefd8",
  "nonce": "0x00000000000000031",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp": "0x00"
}

ここは書籍だともっと項目は少ないですが、変更されているためGithubに書いてあるものを使います。
https://github.com/ethereum/go-ethereum#operating-a-private-network

chainIdは適当に22
nonceも適当な値にしています。

初期化のコマンドは以下です。
geth --datadir /geth/private_net/ init /geth/private_net/genesis.json
datadirはデータの保存先などで使われるディレクトリの指定です。
https://geth.ethereum.org/docs/interface/command-line-options
指定なしだと~/.ethereumなので作成しておいたprivate_net/を指定しておきます。

/geth/private_net # geth --datadir /geth/private_net/ init /geth/private_net/genesis.json
INFO [05-02|06:43:33.332] Maximum peer count                       ETH=50 LES=0 total=50
INFO [05-02|06:43:33.332] Smartcard socket not found, disabling    err="stat /run/pcscd/pcscd.comm: no such file or directory"
WARN [05-02|06:43:33.465] Sanitizing cache to Go's GC limits       provided=1024 updated=662
INFO [05-02|06:43:33.465] Set global gas cap                       cap=50,000,000
INFO [05-02|06:43:33.468] Allocated cache and file handles         database=/geth/private_net/geth/chaindata cache=16.00MiB handles=16
INFO [05-02|06:43:33.583] Writing custom genesis block 
INFO [05-02|06:43:33.583] Persisted trie from memory database      nodes=0 size=0.00B time="27.7µs" gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [05-02|06:43:33.613] Successfully wrote genesis state         database=chaindata                        hash=119a56..09b937
INFO [05-02|06:43:33.613] Allocated cache and file handles         database=/geth/private_net/geth/lightchaindata cache=16.00MiB handles=16
INFO [05-02|06:43:33.724] Writing custom genesis block 
INFO [05-02|06:43:33.726] Persisted trie from memory database      nodes=0 size=0.00B time="22.3µs" gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [05-02|06:43:33.763] Successfully wrote genesis state         database=lightchaindata                        hash=119a56..09b937

Successfully wrote genesis stateが出力されれば成功。
private_net/geth/keystore/が生成されています。

Gethの起動

書籍でのコマンドは以下となっています。

geth --networkid "10" --nodiscover --datadir ~/geth/private_net --rpc --rpcaddr "localhost" --rpcport "8545" --rpccorsdomain "*" --rpcapi "eth,net,web3,personal" --targetgaslimit "20000000" console 2>> ~/geth/private_net/error.log

rpc関連のコマンドはhttpと名前が置き換わっています。
https://github.com/ethereum/go-ethereum/pull/20935

また、プルリク見つけられませんでしたがtargetgaslimitminer.gaslimitに変わっています。

そのため以下となります。

geth --networkid "22" --nodiscover --datadir /geth/private_net \
--http --http.addr "localhost" --http.port "8545" --http.corsdomain "*" \
--http.api "eth,net,web3,personal" --miner.gaslimit "20000000" \
console 2>> /geth/private_net/error.log

(ディレクトリもこの記事では/geth/private_netとしているので変えてます)

パラメータの説明は以下です。
https://geth.ethereum.org/docs/interface/command-line-options

実際は以下のようにエラー以外のログも出力されているのでファイル名はerror.logでなくてもいいかもです。

INFO [05-02|07:26:09.346] Maximum peer count                       ETH=50 LES=0 total=50
INFO [05-02|07:26:09.349] Smartcard socket not found, disabling    err="stat /run/pcscd/pcscd.comm: no such file or directory"
WARN [05-02|07:26:09.382] Sanitizing cache to Go's GC limits       provided=1024 updated=662

起動

/geth # geth --networkid "22" --nodiscover --datadir /geth/private_net --miner.gaslimit "20000000" console 2>> /get
h/private_net/error.log
Welcome to the Geth JavaScript console!

instance: Geth/v1.10.18-unstable-f94e23ca/linux-amd64/go1.18.1
at block: 0 (Thu Jan 01 1970 00:00:00 GMT+0000 (UTC))
 datadir: /geth/private_net
 modules: admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d or type exit
> 

Welcome to the Geth JavaScript console!を出力されれば成功。

アカウントの作成・マイニング

上記でGethを起動するとコンソールが起動しているのでそのままコマンドを入力していきます。
以下は書籍そのままなので簡単に書いていきます。

> personal.newAccount("hogehoge1234")
"0xdcfc0837e489ead966ecdb00abce38d72267c474"
> personal.newAccount("hoge")
"0xac5a53981b38eb63134dfee19934742542ef5874"

出力されているのはアカウント。

アカウント一覧は以下。

>  eth.accounts
["0xdcfc0837e489ead966ecdb00abce38d72267c474", "0xac5a53981b38eb63134dfee19934742542ef5874"]

ブロック生成の報酬を受け取るコインベースアカウントというものが存在します。
ここでは一つ目に作成したアカウントが担っています。

> eth.coinbase
"0xdcfc0837e489ead966ecdb00abce38d72267c474"

↑一つ目のアカウントと同じであることが確認できます。

マイニング
最初はまだブロックがないので0

> eth.blockNumber
0

開始

> miner.start(1)
null

初回はマインニングが開始されるまで時間がかかります。
数分かかりました。

ブロックが生成されています。

> eth.blockNumber
82

コインベースアカウントに報酬としてEtherが入っています。

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

最初、だれも(というかこのネットワークには自分しかいないので自分が)取引をしていないのにどうして勝手にブロックが生成されるんだろうと思いました。
実際は、取引(トランザクション)がなくてもブロックは生成されるということでした。

ブロックに含まれているゼロから複数のトランザクションは、TransactionTreeと呼ばれる木構造に入っています。ブロックは常に時間が経つごとに生成され続けるので、ネットワーク初期やたまたま利用が少ない場合は、トランザクションが入っていない場合もありえます。トランザクションには、メッセージコールとコントラクト生成の2種類が存在します。
加嵜 長門,篠原 航. ブロックチェーンアプリケーション開発の教科書(リフロー版) (Japanese Edition) (p.324). Kindle 版.

Discussion