「Ethereum入門」を Geth 1.6.7 で進めてみた（インストールからetherの送金まで）

# はじめに

[Ethereum入門](https://book.ethereum-jp.net/) というEthereumのチュートリアルに取り組みました。

このチュートリアルではEthereumのクライアントとして [Geth](https://github.com/ethereum/go-ethereum) (go-ethereum, Go製のクライアント) が使われているのですが，Gethのバージョニングは [セマンティック・バージョニング](http://semver.org/lang/ja/) に従っておらず，1.5.x と 1.6.x の間にいくつか破壊的変更も含まれています。つまり 1.5.x 向けに書かれた資料が 1.6.x では動かないことがあります。そのため，1.6.7 でチュートリアルを進める上でそのままでは動作しなかったところを中心に知見をまとめました。

# 筆者の環境

```console
$ geth version
Geth
Version: 1.6.7-stable
Git Commit: ab5646c532292b51e319f290afccf6a44f874372
Architecture: amd64
Protocol Versions: [63 62]
Network Id: 1
Go Version: go1.8.3
Operating System: darwin
GOPATH=/Users/arosh/go
GOROOT=/usr/local/Cellar/go/1.8.3/libexec
```

Geth 1.6.7のコードネームは [AYTABTU](https://github.com/ethereum/go-ethereum/releases/tag/v1.6.7) (All Your Transaction Are Belong To Us) といいます。

# Gethをインストールする

https://install-geth.ethereum.org からインストールする方法は廃止されました。最新のインストール方法はgo-ethereumのGitHub Wikiに書いてあります。

[Building Ethereum · ethereum/go-ethereum Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum)

私はmacOSなのでHomebrewでインストールしました。

```
brew tap ethereum/ethereum
brew install ethereum
```

# プライベート・ネットを作成する

本番のEthereumネットワークを使うには送金やコントラクトのデプロイ，実行などに手数料が発生してしまうので，テスト用途ではプライベート・ネットを作るのが便利です。プライベート・ネットを作成する方法には，自分で `genesis.json` を用意する方法と，`--dev` オプションを使う方法がありますが，複数台のノードでネットワークを構築しないのであれば `--dev` オプションを使う方法のほうが簡単だと思います。

## 自分で `genesis.json` を用意する場合

`genesis.json` のパラメータの意味については以下の投稿を参照してください。

* [go ethereum - What does each genesis.json parameter mean? - Ethereum Stack Exchange](https://ethereum.stackexchange.com/questions/2376/what-does-each-genesis-json-parameter-mean)
* [go ethereum - The meaning/specification of "config" in genesis.json - Ethereum Stack Exchange](https://ethereum.stackexchange.com/questions/15682/the-meaning-specification-of-config-in-genesis-json)

Geth 1.6から`genesis.json`に`config`節を追加する必要があります。GethのREADMEをコピペするのが良いのではないでしょうか。

https://github.com/ethereum/go-ethereum#defining-the-private-genesis-state

```json:genesis.json
{
  "config": {
        "chainId": 10,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
  "alloc"      : {},
  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x20000",
  "extraData"  : "",
  "gasLimit"   : "0x2fefd8",
  "nonce"      : "0x0000000000000042",
  "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00"
}
```

注意しなければならないのは `config.chainID` の値と，Gethを起動する時に使う `--networkid` の値を一致させる必要があることです。これを正しく設定していないとマイニングを行ったときに "invalid sender" というエラーが出ます（参考：[最小構成のEthereum Tokenのprivatenetへの登録](http://qiita.com/KumanoT/items/b7fe65b8512f0d8dd88b)）。

プライベート・ネットを作成するには以下のコマンドを実行します。今回は他のノードとネットワークを構築させないため `--nodiscover` `--maxpeers 0` を指定しています。

```
geth --datadir . genesis.json
geth --datadir . --networkid 10 --nodiscover --maxpeers 0
```

## `--dev` オプションを使う場合

便利なオプションですが，ヘルプには "Developer mode: pre-configured private network with several debugging flags" としか書いておらず，結局ソースコードを読んで挙動を推測しました。さらに悪い事に，古いバージョンのGethとは挙動が変わっているようなので，古い記事を読む際には注意が必要です（参考：[Ethereum go-ethereum/gethでのプライベート環境構築方法について](http://qiita.com/tomohata/items/8a1156397a6b61df9d75)）。

1. `geth init genesis.json` で作成したGenesisブロックは無視される。代わりに特殊な設定のGenesisブロックが利用される [genesis.go#L139](https://github.com/ethereum/go-ethereum/blob/fc5f8a3dda29712c619e9df99e7f8b1ebc2c8ea7/core/genesis.go#L139),
 [genesis.go#L345](https://github.com/ethereum/go-ethereum/blob/fc5f8a3dda29712c619e9df99e7f8b1ebc2c8ea7/core/genesis.go#L345)
2. P2Pネットワークを利用しない設定になる [flags.go#L797](https://github.com/ethereum/go-ethereum/blob/fc5f8a3dda29712c619e9df99e7f8b1ebc2c8ea7/cmd/utils/flags.go#L797)
3. `--datadir` を設定しなかった場合，`/tmp/ethereum_dev_mode` が使用される [flags.go#L819](https://github.com/ethereum/go-ethereum/blob/fc5f8a3dda29712c619e9df99e7f8b1ebc2c8ea7/cmd/utils/flags.go#L797)
4. `--gasprice` を設定しなかった場合 `--gasprice 0` になる [flags.go#L992](https://github.com/ethereum/go-ethereum/blob/fc5f8a3dda29712c619e9df99e7f8b1ebc2c8ea7/cmd/utils/flags.go#L992)

開発環境と本番環境で挙動が大きく変わってしまうため， `--gasprice 0` の設定は変更したほうがよいかもしれません。

プライベート・ネットを作成するには以下のコマンドを実行します。

```
geth --datadir . --dev --gasprice 18000000000
```

# etherを採掘して送金する

ここからしばらくの間は [Ethereum入門](https://book.ethereum-jp.net/first_use/mining_ether.html) の内容がそのまま動作するので説明は省略します。

```
$ geth attach ipc:geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0

# アカウントを作成する
> personal.newAccount("password1")
"0x21baabbd50c4ece4423be3588993bd97e92ef637"

# あと2つ作成する
> personal.newAccount("password2")
"0xd02f002f7f1cf12ca80d99b922db6377dbd60384"
> personal.newAccount("password3")
"0xfd30e3096902bc1f0f98e22e1e72440625c49bbc"

# アカウントの確認
> eth.accounts
["0x21baabbd50c4ece4423be3588993bd97e92ef637", "0xd02f002f7f1cf12ca80d99b922db6377dbd60384", "0xfd30e3096902bc1f0f98e22e1e72440625c49bbc"]

# マイニング報酬を振り込むアカウントの確認
> eth.coinbase
"0x21baabbd50c4ece4423be3588993bd97e92ef637"

# マイニングの開始（実行してからしばらく待つ）
> miner.start(1)
null

# マイニングされたブロックの数
> eth.blockNumber
147

# ブロックの情報の表示
> eth.getBlock(100)
{
  difficulty: 136718,
  extraData: "0xd883010607846765746887676f312e382e338664617277696e",
  gasLimit: 4712388,
  gasUsed: 0,
  hash: "0x1ab0effc87f0fd16bfe23a77b9f1b15577429cbbf81887d6404e8c7c41eac4b6",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0x21baabbd50c4ece4423be3588993bd97e92ef637",
  mixHash: "0xd33adcca8106b4c1bea4bc13ed9a8ba1dd4dd2da49e35c8d1d3a016607b83ef7",
  nonce: "0x2b50413110c99b50",
  number: 100,
  parentHash: "0xdd927202ee0464cf30462f42b442370a57bf6385124d92945e76ffbbf6aa21ea",
  receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 536,
  stateRoot: "0xeb1b9b3c9015d485a59acda530829b838dfc2770836289013aa8b718c6a5d497",
  timestamp: 1504169999,
  totalDifficulty: 13511140,
  transactions: [],
  transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  uncles: []
}

> eth.getBlock(101)
{
  difficulty: 136784,
  extraData: "0xd883010607846765746887676f312e382e338664617277696e",
  gasLimit: 4712388,
  gasUsed: 0,
  hash: "0xdf4e303da71e8d94df6eb046f70f05966595022fc4dab259ebcec48cd8d81a46",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0x21baabbd50c4ece4423be3588993bd97e92ef637",
  mixHash: "0xb01a8c3c0ba8e0abe2bec6d7cf6e9db0cd7d4bee278c0cda8df644dd72f9bcc6",
  nonce: "0x12b956a969d89734",
  number: 101,
  parentHash: "0x1ab0effc87f0fd16bfe23a77b9f1b15577429cbbf81887d6404e8c7c41eac4b6",
  receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 536,
  stateRoot: "0xdaaeadd42f5a310b728cb82e3b0464061ee69bbcdbf4defcc2763361f960c5f1",
  timestamp: 1504170000,
  totalDifficulty: 13647924,
  transactions: [],
  transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  uncles: []
}

# マイニング報酬をether単位で表示
> web3.fromWei(eth.getBalance(eth.coinbase), "ether")
780

# 1番目のアカウント (coinbase) から2番目のアカウントに1 ether送金する
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei("1", "ether")})
Error: authentication needed: password or unlock
    at web3.js:3104:20
    at web3.js:6191:15
    at web3.js:5004:36
    at <anonymous>:1:1

# アカウントをアンロックするのを忘れていた
> personal.unlockAccount(eth.accounts[0], "password1")
true

# 送金をやり直す
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei("1", "ether")})
"0x50f84b46497a9b4eba2cb0eb50bf0d52b3aecbe1407efdd283e362f156c60dde"

# 2番目のアカウントの残高を確認する
> web3.fromWei(eth.getBalance(eth.accounts[1]))
1

# 2番目のアカウントから3番目のアカウントに0.5 ether送金する
> personal.unlockAccount(eth.accounts[1], "password2")
true
> eth.sendTransaction({from: eth.accounts[1], to: eth.accounts[2], value: web3.toWei("0.5", "ether")})
"0xd38d12bff2e17101219374c324213f9f5cfcbb3c5676fca231de641e4d8fc8ec"

# 2番目と3番目のアカウントの残高を確認する
> web3.fromWei(eth.getBalance(eth.accounts[1]), "ether")
0.499622
> web3.fromWei(eth.getBalance(eth.accounts[2]), "ether")
0.5

# 2番目のアカウントの残高が 1 - 0.5 = 0.5 ether よりも少ないのは，送金手数料が発生したため
> eth.getTransaction("0xd38d12bff2e17101219374c324213f9f5cfcbb3c5676fca231de641e4d8fc8ec")
{
  blockHash: "0x66ecfaf846224ff205d641ffcab38104f5acfa0871d902dc53dac5d036ddca35",
  blockNumber: 391,
  from: "0xd02f002f7f1cf12ca80d99b922db6377dbd60384",
  gas: 90000,
  gasPrice: 18000000000,
  hash: "0xd38d12bff2e17101219374c324213f9f5cfcbb3c5676fca231de641e4d8fc8ec",
  input: "0x",
  nonce: 0,
  r: "0x703bb4b15e87c3c0e2078ce2653a8895a121df290127dd091c6e7e68b7afd317",
  s: "0x6429a6a7bb5045457cdaa20257af2eb19d6d06d8230b3c4438f2f61156d693b9",
  to: "0xfd30e3096902bc1f0f98e22e1e72440625c49bbc",
  transactionIndex: 0,
  v: "0xa95",
  value: 500000000000000000
}

> eth.getBlock("0x66ecfaf846224ff205d641ffcab38104f5acfa0871d902dc53dac5d036ddca35")
{
  difficulty: 150262,
  extraData: "0xd883010607846765746887676f312e382e338664617277696e",
  gasLimit: 4712388,
  gasUsed: 21000,
  hash: "0x66ecfaf846224ff205d641ffcab38104f5acfa0871d902dc53dac5d036ddca35",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0x21baabbd50c4ece4423be3588993bd97e92ef637",
  mixHash: "0x0153b82617b97cc391f66607e1a2b48fe1c305558ecebe6e7266777556ad58c5",
  nonce: "0x06a71e24164c1e67",
  number: 391,
  parentHash: "0xee60ab2e3c682e149c537b30e725f5bd852a239cf02ee2ccab8ecbde9ce2c375",
  receiptsRoot: "0xf37332ef7bba33f2050ea36c1ce837faf22df96685615941fb271db79330c5c2",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 654,
  stateRoot: "0x0a15295f8240dbd2ae57be581749e4ee720bb6397cadf1dbfa6ad3ade5b68be0",
  timestamp: 1504171620,
  totalDifficulty: 54678353,
  transactions: ["0xd38d12bff2e17101219374c324213f9f5cfcbb3c5676fca231de641e4d8fc8ec"],
  transactionsRoot: "0xb26180dce26dd5de1a6a32da3c0565f02da6ce3ed71403464a4c1a5453327fd7",
  uncles: []
}

# gas price が 18000000000 wei/gas で，送金に 21000 gas 必要なので，それだけの手数料が引かれている
> eth.gasPrice
18000000000
> web3.toWei("0.5", "ether") - 21000 * eth.gasPrice
499622000000000000
> eth.getBalance(eth.accounts[1])
499622000000000000
```

# おまけ

## Geth以外のクライアント

少し古いですが，Ethereumのクライアントのリストが以下のページにあります。

[Choosing a client — Ethereum Homestead 0.1 documentation](http://ethdocs.org/en/latest/ethereum-clients/choosing-a-client.html)

現在でもメンテナンスされていると思われているクライアントは以下のとおりです。`brew install ethereum` でインストールできることからも分かるように，Go製のクライアントが最も人気があります。

|Repository|Language|GitHub Star|
|----------|--------|-----------|
|https://github.com/ethereum/go-ethereum|Go|6958|
|https://github.com/paritytech/parity|Rust|1951|
|https://github.com/ethereum/cpp-ethereum|C++|1585|
|https://github.com/ethereum/ethereumj|Java|695|
|https://github.com/ethereum/pyethapp|Python|630|

## プライベート・ネット以外のネットワーク

### ライブ・ネット

Ethereumの本番のネットワークです。開発の進行度によってFrontier, Homestead, Metropolis, Serenityという4段階の名前が付けられており，Frontierは2015年7月，Homesteadは2016年3月にリリースされました。次期のバージョンであるMetropolisは2017年中にはリリースされそうです。Serenityではコンセンサス・アルゴリズムがProof of WorkからProof of Stakeに変更される予定だそうですが，どうなるか分かりません。

[イーサリアムには4つの開発段階がある - ビットコインラボ](https://bitcoinlab.jp/articles/424)

接続するには `--networkid 1` を指定します。

```console
$ geth --help
...
--networkid value                          Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby) (default: 1)
...
```

### テスト・ネット

テスト・ネットはテスト用に運用されている開かれたネットワークです。`--networkid` のヘルプにも載っているように，Ethereumのテスト・ネットは複数あります。

[Ethereum Testnet Selection](https://testnet.etherscan.io/)

現在のEthereumはコンセンサス・アルゴリズムとしてProof of Workが使用されています。etherに金銭的価値が生じないテスト・ネットで熱心にマイニングをする人はあまりいないため，簡単に乗っ取ることができるという問題があり，最初のテスト・ネットであるMordenを継続することが困難になったため新たにRopstenの運用が開始されました。

しかしながら，これでも根本的な解決にはなっていないため，Proof of Authorityというコンセンサス・アルゴリズムを使用しているRinkebyも運用されています。Proof of Authorityでは選ばれたコミュニティメンバーのみがブロックを承認することができるという中央集権的な仕組みにより運用されています。Rinkeby用のetherは https://faucet.rinkeby.io/ から入手することができます。

- [From Morden to Ropsten - Ethereum Blog](https://blog.ethereum.org/2016/11/20/from-morden-to-ropsten/)
- [We Have Migrated From Ropsten to Rinkeby – uPort – Medium](https://medium.com/uport/we-have-migrated-from-ropsten-to-rinkeby-6d315b2dc44e)

## マイニングのCPU負荷を下げる

プライベート・ネットで送金やコントラクトのデプロイ，実行を行うためには，いずれかのノードでマイニングを行う必要があります。ラップトップで開発をしている場合には熱くなって嫌なので，CPUの負荷を下げる方法を調べました。

macOSの場合には [cputhrottle](http://www.willnolan.com/cputhrottle/cputhrottle.html) というツールがあり，Homebrewでインストールすることができます。新しいターミナルを開いて以下のコマンドを実行すればCPU負荷を10%に減らすことができるのですが，sudoで実行しないといけないので正直あまりやりたくないですね…

```
sudo cputhrottle $PID 10
```

[macos - Can I manually limit the %CPU used by a process? - Ask Different](https://apple.stackexchange.com/questions/24998/can-i-manually-limit-the-cpu-used-by-a-process)