Ethereumのテストネット上にトークンをデプロイする

「2017年は仮想通貨元年だったが、2018年はEthereum元年だった」

1年後にはそう言われているような気がするEthereum推しのブロックチェーンエンジニア見習いです。
周りの人にもブロックチェーンの可能性というものをぜひとも感じて欲しいのですが、なんせ環境構築がめんどくさいのです...

特に、テストネットに接続する場合はチェーンの同期なども必要で、初学者には敷居が高いです。

そこで、今回は環境構築を行い、テストネットに繋いで、トークンを発行するところまで行ってみました。
（OSはubuntuを利用しています。mac,windowsの場合は適宜読み替えてください。）

##1. 環境構築
###0. 依存ライブラリのインストール

```
$ sudo apt-get install make
$ sudo apt-get install -y build-essential libgmp3-dev golang git tree
```

上記コマンドでインストールされるgolangのバージョンが1.6と古いため、以下の記事を参考にバージョンをupdateしました。
https://tecadmin.net/install-go-on-ubuntu/

node.jsのインストール

```
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
sudo apt-get install nodejs
```

###1. gethをインストールする
今回はgolangで実装されたEthereum Clientであるgo-ethereum(geth)を使用します。
（gethは"ゲス"と呼びます。初めて聞いた人はだいたい、「すごい呼称だなw」的な反応をします...）

リポジトリはこちら↓
https://github.com/ethereum/go-ethereum

1.　go-ethereumをgitリポジトリからクローンする

```
$ git clone https://github.com/ethereum/go-ethereum.git
$ cd go-ethereum
$ git checkout refs/tags/v1.7.3
$ git branch
* (HEAD detached at v1.7.3)
  master
```

2.　ビルドする

```
$ make geth

〜〜（省略）〜〜

Done building.
Run "/home/ubuntu/go-ethereum/build/bin/geth" to launch geth.
```

3.　gethのバージョンを確認する

```
$ ./build/bin/geth version
Geth
Version: 1.7.3-stable
Git Commit: 6c6c7b2af3efdad4d2f64f70f3a724af434bbcd2
Architecture: amd64
Protocol Versions: [63 62]
Network Id: 1
Go Version: go1.9.2
Operating System: linux
GOPATH=/home/ubuntu/Projects/Proj1
GOROOT=/usr/local/go
```

4.　gethを/usr/local/binにコピーする

```
$ sudo cp build/bin/geth /usr/local/bin/
```

パスが通っていることを確認する

```
$ which geth
/usr/local/bin/geth
```
###2. solidityをインストールする

```
$ sudo add-apt-repository ppa:ethereum/ethereum
$ sudo apt-get update
$ sudo apt-get install solc
```

###3. truffleのインストール

スマートコントラクトでよく使われるtruffleという開発用のフレームワークをインストールします。

```
$ sudo npm install -g truffle
Truffle v4.0.4 - a development framework for Ethereum
$ truffle
Usage: truffle <command> [options]

Commands:
  init      Initialize new Ethereum project with example contracts and tests
  compile   Compile contract source files
  migrate   Run migrations to deploy contracts
  deploy    (alias for migrate)
  build     Execute build pipeline (if configuration present)
  test      Run Mocha and Solidity tests
  debug     Interactively debug any transaction on the blockchain (experimental)
  opcode    Print the compiled opcodes for a given contract
  console   Run a console with contract abstractions and commands available
  develop   Open a console with a local TestRPC
  create    Helper to create new contracts, migrations and tests
  install   Install a package from the Ethereum Package Registry
  publish   Publish a package to the Ethereum Package Registry
  networks  Show addresses for deployed contracts on each network
  watch     Watch filesystem for changes and rebuild the project automatically
  serve     Serve the build directory on localhost and watch for changes
  exec      Execute a JS module within this Truffle environment
  unbox     Unbox Truffle project
  version   Show version number and exit

See more at http://truffleframework.com/docs
```

これで、環境構築は完了です！


##2. gethをテストネットで起動する

1.　gethをtestnet(Rinkeby)で起動する
今回はlightモードで同期することにします。

```
$ geth --rinkeby --datadir "~/light" --syncmode "light" --rpc --rpcaddr "localhost" --rpcport "8545"
WARN [01-16|08:59:07] No etherbase set and no accounts found as default 
INFO [01-16|08:59:07] Starting peer-to-peer node               instance=Geth/v1.7.3-stable/darwin-amd64/go1.9
INFO [01-16|08:59:07] Allocated cache and file handles         database=/Users/user1/light/geth/lightchaindata cache=128 handles=1024
INFO [01-16|08:59:07] Writing custom genesis block 
INFO [01-16|08:59:07] Initialised chain configuration          config="{ChainID: 4 Homestead: 1 DAO: <nil> DAOSupport: true EIP150: 2 EIP155: 3 EIP158: 3 Byzantium: 1035301 Engine: clique}"
INFO [01-16|08:59:07] Loaded most recent local header          number=0 hash=6341fd…67e177 td=1
INFO [01-16|08:59:07] Starting P2P networking 
INFO [01-16|08:59:09] UDP listener up                          self=enode://a0c8c927d2dfc7ac35af13fa3da050423ca9b76b4309508a8ce6eb99d9f7043a1d9fd9e29adb84ee76050ea4d481f8ec059b9b9fe60d2a40e05d7af878f3d457@[::]:30303
WARN [01-16|08:59:09] Light client mode is an experimental feature 
INFO [01-16|08:59:09] RLPx listener up                         self=enode://a0c8c927d2dfc7ac35af13fa3da050423ca9b76b4309508a8ce6eb99d9f7043a1d9fd9e29adb84ee76050ea4d481f8ec059b9b9fe60d2a40e05d7af878f3d457@[::]:30303
INFO [01-16|08:59:09] IPC endpoint opened: /Users/user1/light/geth.ipc 
INFO [01-16|08:59:09] HTTP endpoint opened: http://localhost:8545 
INFO [01-16|08:59:31] Block synchronisation started 
INFO [01-16|08:59:33] Imported new block headers               count=192 elapsed=78.489ms number=192 hash=8c570c…ba360c ignored=0
INFO [01-16|08:59:33] Imported new block headers               count=192 elapsed=130.435ms number=384 hash=6d95fa…a59e49 ignored=0
INFO [01-16|08:59:34] Imported new block headers               count=192 elapsed=97.650ms  number=576 hash=8cb307…7e165a ignored=0
INFO [01-16|08:59:34] Imported new block headers               count=192 elapsed=107.089ms number=768 hash=d2f106…d25b87 ignored=0
INFO [01-16|08:59:34] Imported new block headers               count=576 elapsed=296.116ms number=1344 hash=4524ae…5d3fff ignored=0
INFO [01-16|08:59:35] Imported new block headers               count=192 elapsed=204.562ms number=1536 hash=ebcd68…10b559 ignored=0
INFO [01-16|08:59:35] Imported new block headers               count=192 elapsed=80.962ms  number=1728 hash=7eff72…2707a2 ignored=0
```

2.　gethのコンソールに接続する
geth.ipcの置いてあるパスを指定してください。

```
$ geth attach ipc://Users/user1/light/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.7.3-stable/darwin-amd64/go1.9
 modules: admin:1.0 debug:1.0 eth:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

> 
```

同期が始まると、以下のようにブロックの状況が確認できます。
falseとなる場合は同期が始まっていないので、時間を置いて上記コマンドを再実行してください。

```
> eth.syncing
{
  currentBlock: 386688,
  highestBlock: 1599403,
  knownStates: 0,
  pulledStates: 0,
  startingBlock: 0
}
```

アカウントを作成して、ロックを解除しておきます。

```
> personal.newAccount('pass0')
"0x9674320d61e2dab1c0dcbe5bf6c7992cb14426a2"
> personal.unlockAccount(eth.accounts[0], 'pass0')
true
```

テスト用のEtherを取得します。
rinkebyのテストネットのEtherは以下のページから取得することができます。
https://www.rinkeby.io/#faucet

「How does this work?」部分のtweetのリンクをクリックします。

![スクリーンショット 2018-01-14 21.29.37.png](https://qiita-image-store.s3.amazonaws.com/0/129706/84ea734a-0e0b-a650-44cd-a3c7284f51a6.png)

「0x0000....」の部分に先ほど作成したアドレス（"0x9674320d61e2dab1c0dcbe5bf6c7992cb14426a2"）を入れてツイートします。

<img width="682" alt="スクリーンショット 2018-01-16 16.10.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/129706/c8686c84-d7a0-063c-1607-bb7391e5e02c.png">

このツイートの「ツイートへのリンクをコピー」をクリックします。

![スクリーンショット 2018-01-14 21.30.24.png](https://qiita-image-store.s3.amazonaws.com/0/129706/777701e5-37ac-5289-c699-301169a5851a.png)

このツイートへのリンクをテキストボックスに貼り付けて「Give me Ether」をクリックするとetherを手に入れることができます。

<img width="664" alt="スクリーンショット 2018-01-16 16.13.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/129706/8c594576-b32a-5cc7-0b2a-499ac63d3e64.png">


gethの同期が完了すると、手に入れたetherが反映されます。

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

Ethereumはパブリックなブロックチェーンなので、ブラウザでも[etherscan](https://rinkeby.etherscan.io/)というサイトでもトランザクションを確認することができます。

<img width="1197" alt="スクリーンショット 2018-01-16 17.12.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/129706/b7ff8ec6-971a-93fa-8b03-733875b0de28.png">

##3. トークンを作成する
いよいよトークンを作成します。

1.　ディレクトリを作成して、truffleを初期化する

```
$ mkdir Test_Token
$ cd Test_Token
$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test
```

2.　npmを使って、zeppelin-solidityを導入する
これはトークンを作成するためのライブラリで、トークン作成では、バグが入り込まないように、よく作り込まれているライブラリをなるべく使うことが推奨されています。

```
npm init
npm install zeppelin-solidity --save
```

3.　トークンをプログラミングする
（Test_Token/contracts/TestToken.sol）

```solidity:TestToken.sol
pragma solidity ^0.4.17;
import "zeppelin-solidity/contracts/token/StandardToken.sol";

contract TestToken is StandardToken {
  string public name = "ShikiCoin";
  string public symbol = "SKC";
  uint public decimals = 18;

  function TestToken(uint initialSupply) public {
    totalSupply = initialSupply;
    balances[msg.sender] = initialSupply;
  }
}
```

4.　マイグレーションファイルの作成
（Test_Token/migrations/2_deploy_contracts.js）

```javascript:2_deploy_contracts.js
const Token = artifacts.require('./TestToken.sol')

module.exports = (deployer) => {
  let initialSupply = 50000e18
  deployer.deploy(Token, initialSupply)
}
```

5.　truffle.jsの設定
truffle.jsを以下のように設定します。
（Test_Token/truffle.js）

```javascript:truffle.js
module.exports = {
  networks: {
    rinkeby: {
      host: "localhost", // Connect to geth on the specified
      port: 8545,
      from: "0x9674320d61e2dab1c0dcbe5bf6c7992cb14426a2", // default address to use for any transaction Truffle makes during migrations
      network_id: 4,
      gas: 4612388 // Gas limit used for deploys
    }
  }
};
```

6.　コントラクトをコンパイルする

```
$ truffle compile
Compiling ./contracts/Migrations.sol...
Compiling ./contracts/TestToken.sol...
Compiling zeppelin-solidity/contracts/math/SafeMath.sol...
Compiling zeppelin-solidity/contracts/token/BasicToken.sol...
Compiling zeppelin-solidity/contracts/token/ERC20.sol...
Compiling zeppelin-solidity/contracts/token/ERC20Basic.sol...
Compiling zeppelin-solidity/contracts/token/StandardToken.sol...
Writing artifacts to ./build/contracts
```

7.　コントラクトをデプロイする
いよいよデプロイします。

```
$ truffle migrate --network rinkeby
Using network 'rinkeby'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0xdb52088a39f4749361bca74fc526c9789e22b692466a441ac96e99ff61bcf986
/usr/local/lib/node_modules/truffle/build/cli.bundled.js:128717
var Module;if(!Module)Module=(typeof Module!=="undefined"?Module:null)||{};var moduleOverrides={};for(var key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;if(Module["ENVIRONMENT"]){if(Module["ENVIRONMENT"]==="WEB"){ENVIRONMENT_IS_WEB=true}else if(Module["ENVIRONMENT"]==="WORKER"){ENVIRONMENT_IS_WORKER=true}else if(Module["ENVIRONMENT"]==="NODE"){ENVIRONMENT_IS_NODE=true}else if(Module["ENVIRONMENT"]==="SHELL"){ENVIRONMENT_IS_SHELL=true}else{throw new Error("The provided Module['ENVIRONMENT'] value is not valid. It must be one of: WEB|WORKER|NODE|SHELL.")}}else{ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&"function"==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL=!

Error: invalid address
    at inputAddressFormatter (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:33350:11)
    at /usr/local/lib/node_modules/truffle/build/cli.bundled.js:37938:28
    at Array.map (<anonymous>)
    at Method.formatInput (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:37937:32)
    at Method.toPayload (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:37963:23)
    at Eth.send [as getCode] (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:37988:30)
    at Object.callback (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:327570:39)
    at /usr/local/lib/node_modules/truffle/build/cli.bundled.js:37991:25
    at /usr/local/lib/node_modules/truffle/build/cli.bundled.js:329533:9
    at /usr/local/lib/node_modules/truffle/build/cli.bundled.js:176186:11
```

エラーになった...

ググってみると、どうやら"light"モードでgethを起動していると、デプロイできないようです。

ということでgethを通常モードで起動し直します。

```
$ geth --rinkeby --rpc --rpcaddr "localhost" --rpcport "8545"
INFO [01-16|15:11:48] Starting peer-to-peer node               instance=Geth/v1.7.3-stable/darwin-amd64/go1.9
INFO [01-16|15:11:48] Allocated cache and file handles         database=/Users/user1/Library/Ethereum/rinkeby/geth/chaindata cache=128 handles=1024
INFO [01-16|15:11:49] Initialised chain configuration          config="{ChainID: 4 Homestead: 1 DAO: <nil> DAOSupport: true EIP150: 2 EIP155: 3 EIP158: 3 Byzantium: 1035301 Engine: clique}"
INFO [01-16|15:11:49] Initialising Ethereum protocol           versions="[63 62]" network=4
INFO [01-16|15:11:49] Loaded most recent local header          number=1211507 hash=df13be…773045 td=2355536
INFO [01-16|15:11:49] Loaded most recent local full block      number=1211507 hash=df13be…773045 td=2355536
INFO [01-16|15:11:49] Loaded most recent local fast block      number=1211507 hash=df13be…773045 td=2355536
INFO [01-16|15:11:49] Loaded local transaction journal         transactions=0 dropped=0
INFO [01-16|15:11:49] Regenerated local transaction journal    transactions=0 accounts=0
WARN [01-16|15:11:49] Blockchain not empty, fast sync disabled 
INFO [01-16|15:11:49] Starting P2P networking 
INFO [01-16|15:11:51] UDP listener up                          self=enode://6d405239514faf1a61e7f9498eb16b6a60d55daf1c5372ef7a98fa4cad2b93b0bffbcd3eda0e90e6cb9227490b00debd27af2947b8688ddc03c1d31949e8b299@10.41.106.185:30303
INFO [01-16|15:11:51] RLPx listener up                         self=enode://6d405239514faf1a61e7f9498eb16b6a60d55daf1c5372ef7a98fa4cad2b93b0bffbcd3eda0e90e6cb9227490b00debd27af2947b8688ddc03c1d31949e8b299@10.41.106.185:30303
INFO [01-16|15:11:51] IPC endpoint opened: /Users/user1/Library/Ethereum/rinkeby/geth.ipc 
INFO [01-16|15:11:51] HTTP endpoint opened: http://localhost:8545 
INFO [01-16|15:11:51] Mapped network port                      proto=udp extport=30303 intport=30303 interface="UPNP IGDv1-PPP1"
INFO [01-16|15:11:51] Mapped network port                      proto=tcp extport=30303 intport=30303 interface="UPNP IGDv1-PPP1"
INFO [01-16|15:12:21] Block synchronisation started 
INFO [01-16|15:12:24] Imported new chain segment               blocks=4 txs=478 mgas=29.344 elapsed=507.784ms mgasps=57.789 number=1211511 hash=b5e8b5…66d7b4
INFO [01-16|15:12:25] Imported new chain segment               blocks=7 txs=1001 mgas=51.577 elapsed=640.520ms mgasps=80.523 number=1211518 hash=9bc858…231a52
INFO [01-16|15:12:25] Imported new chain segment               blocks=42 txs=838  mgas=76.407 elapsed=759.358ms mgasps=100.620 number=1211560 hash=89c2f6…76ba0e
INFO [01-16|15:12:31] Imported new chain segment               blocks=351 txs=5541 mgas=494.349 elapsed=4.874s    mgasps=101.406 number=1211911 hash=2eeedd…f06b38
```

同期が完了してから、もう一度デプロイしてみる。

```
$ truffle migrate --network rinkeby
Using network 'rinkeby'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x5eec16a9f39bb0187e689840ad621b06448fabea895a2822a9222f52a0a8747a
  Migrations: 0x7514c7b8343c48bddfa46371e7027d6413bf0e0f
Saving successful migration to network...
  ... 0xaeb6c2cfa95064edd928e61bfb6a3e93f96a63ebf33173bfee8fe7aebc7c110d
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying TestToken...
  ... 0xbb1ab70b85edccc213fc196e043399c76207b08f63aa4ac956a177222173351a
  TestToken: 0xc46d99276b04dbfe2e9cb25a3bdca66c33e0e300
Saving successful migration to network...
  ... 0x0e7f705902be500dea1c6ff0b1f7616c041028660afd9a33ba2a78c709e5e647
Saving artifacts...
```

キター！！！

etherscanで確認してみます。

まずは、アカウントの残高から

<img width="1178" alt="スクリーンショット 2018-01-16 17.30.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/129706/60179b62-6d34-d113-c4cd-017a5d63a7ec.png">

コントラクトをデプロイしたのでETH Balanceが7.5 Etherから7.3211802 Etherに減っていることが分かります。

続いて、Coinの情報を見てみます。
Coinのアドレスはデプロイしたときに帰ってきた値"0xc46d99276b04dbfe2e9cb25a3bdca66c33e0e300"なので、このアドレスを検索します。

<img width="1201" alt="スクリーンショット 2018-01-16 17.29.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/129706/312cfbcc-068d-f8c8-489f-abef9b8da2f2.png">

ちゃんと"ShikiCoin"という名前で新しく通貨が発行できています！！！

##4. まとめ
今回はEthereumのテストネットを使って、独自通貨（トークン）を発行し、パブリックなネットワークにデプロイするところまで確認しました。

もちろん、この通貨はテストネットなので、実際に価値のあるEtherと交換できませんし、メインネットにデプロイしても誰も買ってくれないので、価値はありません（笑）

しかし、誰もが独自の通貨を発行できるようになったというのは非常に画期的なことでしょう。

あとは、この通貨を使ってどんなサービスをユーザーに提供できるか、アイディア次第です。