[Ethereum]Gethでプライベートネットワークを構築しアカウント作成とマイニングをしてみる

# はじめに
Ethereumのスマートコントラクトを実装する時、ネットワーク上にデプロイして動作確認したいことは多いです。
パブリックネットワーク(メインネットワークやテストネットワーク)を使うとgas費用がかかり簡単に動作検証することが出来ないため、Ethereumではローカル環境に簡単に作成できるプライベートネットワークがあります。
今回はローカル環境でEthereumプライベートネットワーク環境を構築しアカウントを作成、そしてマイニングをすることでEtherを取得するところまで記述してみようと思います。


# 作業環境の構築
前回の記事でDockerコンテナにGethをインストールするところまではやりましたのでその続きからやっていきます。
Gethのインストールがまだの方は先にコッチを読んでください ⇒ [【Ethereum】DockerコンテナにGethの環境を構築してみる](https://qiita.com/y_kitchens/items/8eddca53ee4d0f6bb497)

まずコンテナとマウントしている作業フォルダ内に2つのファイルを作成します。ファイル名は任意です。
・genesis.json: genesisファイル
・geth.log: log出力用ファイル

genesisファイルとは、今回gethを使用しEthereumプライベートネットワークのブロックチェーンを作成します。そのためブロックチェーンを作成するにあたり一番最初のブロック(genesisブロック)に関する設定をするためのファイルです。
Ethereumのメインネットワークやテストネットワークでは既にブロックチェーンが作成・運用されているので不要なファイルです。

ファイルを作成したらgenesis.jsonに以下の内容を記述します。

```json:genesis.json
{
  "config": {
    "chainId": 20,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "istanbulBlock": 0,
    "berlinBlock": 0
  },
  "nonce": "0x0000000000000042",
  "timestamp": "0x0",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "extraData": "",
  "gasLimit": "0x8000000",
  "difficulty": "0x4000",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "coinbase": "0x3333333333333333333333333333333333333333",
  "alloc": {}
}
```

geth.logは空のままでOKです。

ファイルを作成したら作業フォルダはこんな感じの構成になっていると思います。

```bash
$ ls
genesis.json  geth.log
```


# genesisファイルのイニシャライズ
準備が出来たらgenesisファイルをイニシャライズし、コンテナの作業フォルダにgethの環境を構築します。
gethコマンドを実行しイニシャライズします。

```bash:geth.log
$ geth --datadir /work init /work/genesis.json
INFO [09-26|01:15:24.108] Maximum peer count                       ETH=50 LES=0 total=50
INFO [09-26|01:15:24.117] Smartcard socket not found, disabling    err="stat /run/pcscd/pcscd.comm: no such file or directory"
WARN [09-26|01:15:24.173] Sanitizing cache to Go\'s GC limits       provided=1024 updated=662
INFO [09-26|01:15:24.173] Set global gas cap                       cap=50,000,000
INFO [09-26|01:15:24.182] Allocated cache and file handles         database=/work/geth/chaindata cache=16.00MiB handles=16
INFO [09-26|01:15:24.339] Writing custom genesis block 
INFO [09-26|01:15:24.343] Persisted trie from memory database      nodes=0 size=0.00B time="355.4µs" gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [09-26|01:15:24.366] Successfully wrote genesis state         database=chaindata            hash=7b2e8b..7e0432
INFO [09-26|01:15:24.366] Allocated cache and file handles         database=/work/geth/lightchaindata cache=16.00MiB handles=16
INFO [09-26|01:15:24.497] Writing custom genesis block 
INFO [09-26|01:15:24.498] Persisted trie from memory database      nodes=0 size=0.00B time="19.8µs"  gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [09-26|01:15:24.517] Successfully wrote genesis state         database=lightchaindata            hash=7b2e8b..7e0432
```
"Successfully wrote genesis state"と表示されればイニシャライズは完了です。

イニシャライズが完了したら作業フォルダの状況を確認してみます。

```bash
$ ls -l
total 4
-rw-r--r-- 1 root root 597 Sep 25 14:32 genesis.json
drwx------ 6 root root 192 Sep 26 01:15 geth
-rw-r--r-- 1 root root   0 Sep 25 14:33 geth.log
drwx------ 2 root root  64 Sep 26 01:15 keystore
```
新たにgethとkeystoreというディレクトリが作成されています。
それぞれの役割は、
・geth: このノードに関する情報やブロックチェーンデータを格納するフォルダ
・keystore: 秘密鍵を格納するフォルダ。プライベート環境でアカウントを作成するとここに秘密鍵が格納される
という感じです。


# gethを起動
gethのイニシャライズが完了したらgethを起動してみます。

```bash
$ geth --networkid "20" --nodiscover --datadir "/work" console 2>> "/work/geth.log"
Welcome to the Geth JavaScript console!

instance: Geth/v1.10.7-stable-12f0ff40/linux-amd64/go1.16.4
at block: 0 (Thu Jan 01 1970 00:00:00 GMT+0000 (UTC))
 datadir: /work
 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
> 
```
コンソールが立ち上がれば起動は成功です。

各コマンドオプションの内容は、
・--networkid: genesis.jsonのchainIdで指定した値を設定します。ethereumブロックチェーンネットワークはパブリックチェーン(メインネットワークやテストネットワーク)からプライベートチェーンまでいくつか種類があります。パブリックチェーン(メインネットワークやテストネットワーク)には固有のネットワークidが割り振られているのでそれ以外を指定します。
・--nodiscover: gethは起動するとnetworkidで指定した値を指定している他のノードを探索し始めます。このオプションでその探索を行わないようにします。
・--datadir: 作業フォルダを指定。
・console: gethを対話的に実行できるコンソールを開きます。
・2>> : ログを抽出します。

作業フォルダを確認すると新たなフォルダが作成されています。

```bash
$ ls -l
total 32
-rw-r--r--  1 yasuhiro  staff   597  9 25 23:32 genesis.json
drwx------  9 yasuhiro  staff   288  9 26 11:16 geth
srw-------  1 yasuhiro  staff     0  9 26 11:16 geth.ipc
-rw-r--r--  1 yasuhiro  staff  6493  9 26 11:16 geth.log
-rw-------  1 yasuhiro  staff    28  9 26 11:15 history
drwx------  2 yasuhiro  staff    64  9 26 10:15 keystore
```
新たにgeth.ipcフォルダとhistoryファイルが作成されています。
・ipcフォルダ: geth起動時にコンソールを起動したので作成されました。
・history: コンソールで実行されたコマンドが記載されるっぽい

今はまだトランザクションも作成していないしブロックのマイニングも行っていないためブロックチェーンにはジェネシスブロック(1番目のブロック)しかありません。
ブロックチェーンの1つ目のブロックの内容を確認してみます。

```bash:gethコンソール
> eth.getBlock(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: []
}
```
genesis.jsonで設定した値が反映されています。

ただ新しいブロックチェーンを作成した段階なので、アカウント(EOA)もスマートコントラクトも設定されていません。

```bash:gethコンソール
> eth.accounts
[]
```

# アカウントの作成
さっそくアカウントを作成してみます
personal.newAccount()で新しいアカウントを作成できます。
newAccountの引数にはアカウントのパスワードを文字列で渡します。

```bash:gethコンソール
> personal.newAccount("test1")
"0x477f0f906f93949501d4c0ef42c260259d1ad030"
```

新しいアカウントが作成できました。eth.accountsで確認してみます。

```bash:gethコンソール
> eth.accounts
["0x477f0f906f93949501d4c0ef42c260259d1ad030"]
```

アカウントが出来たらログを確認してみます。

```text:geth.log
INFO [09-26|02:46:22.781] Your new key was generated               address=0x477F0f906F93949501d4C0ef42C260259D1ad030
WARN [09-26|02:46:22.782] Please backup your key file!             path=/work/keystore/
```
key fileが格納されているようなので作業フォルダのkeystoreを確認してみます

```bash
$ cd keystore/
$ ls -l
total 8
-rw-------  1 yasuhiro  staff  491  9 26 11:46 UTC--2021-09-26T02-46-18.309071400Z--477f0f906f93949501d4c0ef42c260259d1ad030
```

新しくアカウント情報が記載されているJSONファイルが作成されています。
外部ウォレットでこのアカウントを使用したい場合はこのJSONファイルを使用するらしいです。(Metamaskで試してみたがまだ上手くアカウント連携が出来ていません。近いうちMetamaskの使用方法を調べて記事にしたいと思います。)

1つ目のアカウントを作成出来たのでとりあえずアカウントを3つ作成しておきます。

```bash:gethコンソール
> personal.newAccount("test2")
"0x1851ca62fa8353546e32ef2239b4bc461b16a60d"
> 
> personal.newAccount("test3")
"0xfe1fc7af662327a0bc9ab4728c7c48c8782068c7"
> 
> eth.accounts
["0x477f0f906f93949501d4c0ef42c260259d1ad030", "0x1851ca62fa8353546e32ef2239b4bc461b16a60d", "0xfe1fc7af662327a0bc9ab4728c7c48c8782068c7"]
```

アカウントが3つ出来ました。

今回作成時に使用したパスワードはアカウントロックを解除する際に使用するので作業フォルダに適当なファイルを作成しそれぞれのパスワードを保存しておきます。
今回はpassword.txtを作成し、その中にパスワードを順番に記載しておきます。

```text:password.txt
test1
test2
test3
```

# HTTP SERVERの起動
アカウントが作成されたので、web3.jsでgethにアクセスしてみます。
web3.jsのインストール方法や使い方についてはコチラを読んでみてください ⇒ [Web3.jsを使ってみる](https://qiita.com/y_kitchens/items/2802b9373110bed31b74)

web3.jsを使用するので、HTTP SERVERも起動しておきます。
一度gethを終了しコンソールを抜けます。

HTTP SERVERを起動するには --httpオプションでHTTP SERVERを有効化します
※以前までは--rpcを使用していましたが2021年6月で廃止されたようなのでこれからは--httpを使用します。--rpcaddr等もそれぞれ対応する--http.addr等に置き換えて実行します。

```bash
$ geth --networkid "20" --nodiscover --datadir "/work" console 2>> "/work/geth.log" --http --http.addr "localhost" --http.port "8545" --http.api "eth,net,web3,personal,miner,admin" --http.corsdomain "*"
```
これを実行すると

```text:geth.log
INFO [09-26|02:16:42.413] Maximum peer count                       ETH=50 LES=0 total=50
INFO [09-26|02:16:42.415] Smartcard socket not found, disabling    err="stat /run/pcscd/pcscd.comm: no such file or directory"
WARN [09-26|02:16:42.468] Sanitizing cache to Go's GC limits       provided=1024 updated=662
INFO [09-26|02:16:42.470] Set global gas cap                       cap=50,000,000
INFO [09-26|02:16:42.474] Allocated trie memory caches             clean=99.00MiB dirty=165.00MiB
INFO [09-26|02:16:42.477] Allocated cache and file handles         database=/work/geth/chaindata cache=329.00MiB handles=524,288
INFO [09-26|02:16:42.575] Opened ancient database                  database=/work/geth/chaindata/ancient readonly=false
INFO [09-26|02:16:42.581] Initialised chain configuration          config="{ChainID: 20 Homestead: 0 DAO: <nil> DAOSupport: false EIP150: 0 EIP155: 0 EIP158: 0 Byzantium: 0 Constantinople: 0 Petersburg: 0 Istanbul: 0, Muir Glacier: <nil>, Berlin: 0, London: <nil>, Engine: unknown}"
INFO [09-26|02:16:42.613] Disk storage enabled for ethash caches   dir=/work/geth/ethash count=3
INFO [09-26|02:16:42.614] Disk storage enabled for ethash DAGs     dir=/root/.ethash     count=2
INFO [09-26|02:16:42.615] Initialising Ethereum protocol           network=20 dbversion=8
INFO [09-26|02:16:42.627] Loaded most recent local header          number=0 hash=7b2e8b..7e0432 td=16384 age=52y5mo3w
INFO [09-26|02:16:42.630] Loaded most recent local full block      number=0 hash=7b2e8b..7e0432 td=16384 age=52y5mo3w
INFO [09-26|02:16:42.631] Loaded most recent local fast block      number=0 hash=7b2e8b..7e0432 td=16384 age=52y5mo3w
INFO [09-26|02:16:42.636] Loaded local transaction journal         transactions=0 dropped=0
INFO [09-26|02:16:42.645] Regenerated local transaction journal    transactions=0 accounts=0
INFO [09-26|02:16:42.650] Gasprice oracle is ignoring threshold set threshold=2
INFO [09-26|02:16:42.653] Starting peer-to-peer node               instance=Geth/v1.10.7-stable-12f0ff40/linux-amd64/go1.16.4
INFO [09-26|02:16:42.738] New local node record                    seq=2 id=8f2ba8a8bdb73c35 ip=127.0.0.1 udp=0 tcp=30303
INFO [09-26|02:16:42.739] Started P2P networking                   self="enode://859ce4a5dde9e0b50a5abb15daf21da6c44ab0127919fe562b5dff34dab56e99a2960908ed8dd93230b341e74a4199ce8f860af4760dcb4e6061546c8446d4d3@127.0.0.1:30303?discport=0"
INFO [09-26|02:16:42.745] IPC endpoint opened                      url=/work/geth.ipc
INFO [09-26|02:16:42.756] HTTP server started                      endpoint=127.0.0.1:8545 prefix= cors=* vhosts=localhost
WARN [09-26|02:16:42.835] Served eth_coinbase                      reqid=3 t="115.5µs" err="etherbase must be explicitly specified"
```
下から2行目でHTTP SERVERの起動が確認出来ます。これでgethコンソールだけでなくweb3.jsも使用出来ます。

それではgethノードにアクセスするaccess_geth.jsを作成します。

```javascript:access_geth.js
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

// アカウントを取得する
web3.eth.getAccounts().then(console.log);
```
実行

```bash:gethコンソール
$ node access_geth.js 
[ '0x477F0f906F93949501d4C0ef42C260259D1ad030',
  '0x1851Ca62fa8353546E32ef2239b4BC461b16A60D',
  '0xFE1Fc7af662327a0bC9aB4728c7C48C8782068C7' ]
```
ちゃんとgethコンソールと同じアカウントアドレスが返ってきました。
HTTP SERVERも動作しているようです。


# マイニングしEtherをゲットする
今は各アカウントは誰もEtherを所持していません。
プライベートネット環境は自分1人しかいないので簡単にマイニングができます。
なのでマイニングして残高を増やします。

ところで現況は1つのノードに3つアカウントアドレスがある状態です。
マイニングはどのアカウントで行われるのでしょう。確かめてみます。

```bash:gethコンソール
> eth.coinbase
"0x477f0f906f93949501d4c0ef42c260259d1ad030"
```
eth.coinbaseはマイニングを実行するアドレスが格納される属性です。
gethではデフォルトでeth.accounts[0]のアドレスが格納されています。

それではマイニングをスタートします。
miner.start()でマイニングを開始できます。

```bash:gethコンソール
> miner.start()
null
```
nullが返ってくるので不安ですが、ログを見てみましょう。

```text:geth.log
INFO [09-26|04:08:36.963] Commit new mining work                   number=1 sealhash=5d57c8..fbd76c uncles=0 txs=0 gas=0 fees=0 elapsed=2.384ms
INFO [09-26|04:08:40.627] Generating DAG in progress               epoch=0 percentage=0 elapsed=2.922s
INFO [09-26|04:08:44.099] Generating DAG in progress               epoch=0 percentage=1 elapsed=6.393s
INFO [09-26|04:08:47.308] Generating DAG in progress               epoch=0 percentage=2 elapsed=9.603s
```
マイニングはスタートしています。
一番最初はDAGファイルを作成するので時間がかかります。
percentage=100になるとマイニングが始まりますので待ちます。

```text:geth.log
INFO [09-26|04:13:58.141] Successfully sealed new block            number=1 sealhash=5d57c8..fbd76c hash=8b6033..b33374 elapsed=5m21.550s
INFO [09-26|04:13:58.142] 🔨 mined potential block                  number=1 hash=8b6033..b33374
INFO [09-26|04:14:23.976] Successfully sealed new block            number=2 sealhash=2b3d69..a458e4 hash=76002f..a27afe elapsed=25.844s
INFO [09-26|04:14:24.001] 🔨 mined potential block                  number=2 hash=76002f..a27afe
INFO [09-26|04:14:23.979] Commit new mining work                   number=3 sealhash=9e542d..71c0ed uncles=0 txs=0 gas=0 fees=0 elapsed=1.686ms
INFO [09-26|04:14:28.557] Successfully sealed new block            number=3 sealhash=9e542d..71c0ed hash=1aea21..59bacf elapsed=4.578s
INFO [09-26|04:14:28.567] 🔨 mined potential block                  number=3 hash=1aea21..59bacf
```
DAGファイルの作成が終わるとマイニングが始まります。
プライベートネットワークは自分でマイニングするのでマイニングスピードも早いです。

ある程度マイニングしたらマイニングをストップしてみます。
miner.stop()でマイニングを終了できます。

```bash:gethコンソール
> miner.stop()
null
```
nullが返ってくるので不安ですが、これでマイニングはストップします。

作成されたブロック数とアカウント残高を確認してみます。

```javascript:access_geth.js

const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

// ブロック数を取得する
web3.eth.getBlockNumber((error, result) => {
  console.log('block number: ', result);
})


// アカウントの残高を取得する
web3.eth.getAccounts((error, result) => {
  web3.eth.getBalance(result[0], 'latest', (error, response) => {
    console.log('balance: ', response);
  })
})
```
実行

```bash
$ node access_geth.js 
block number:  29
balance:  58000000000000000000
```
29個のブロックが作成され、eth.accounts[0]のアドレスは58000000000000000000weiのEther残高を得たようです。

これで他のアカウントアドレスにEtherを送金してあげれば全てのアカウントがEtherを所持することが出来ますね。


# おわりに
ローカル環境にEthereumプライベートネットワークが構築できました。これで簡単にスマートコントラクトの動作確認を実施できます。
スマートコントラクトの作成やデプロイ方法についてはコチラを読んでみてください ⇒ [【Ethereum】web3.js を使用してプライベートネットにスマートコントラクトをデプロイする](https://qiita.com/y_kitchens/items/9853a91672767ccecb26)

今は色々試しながらEthereumについて学んでいる最中ですので気付いた点や間違えている点があったらご指摘いただけると嬉しいです。
最後までご覧いただきありがとうございました。

# 参考文献、サイト
[Ethereum入門](https://book.ethereum-jp.net/)
このサイトにはホントお世話になっています。EthereumやGethのことが細かく紹介されていますのでご覧になってみてください。

[ブロックチェーンアプリケーション開発の教科書](https://www.amazon.co.jp/%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E3%83%81%E3%82%A7%E3%83%BC%E3%83%B3%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E9%96%8B%E7%99%BA%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-%E5%8A%A0%E5%B5%9C-%E9%95%B7%E9%96%80/dp/4839965137)
この本はEthereumを使ったDApp開発のことがかなり網羅されているので、おすすめです。