Blockchain
Ethereum
geth
ERC20

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

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

スクリーンショット 2018-01-16 16.10.15.png

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

スクリーンショット 2018-01-14 21.30.24.png

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

スクリーンショット 2018-01-16 16.13.01.png

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

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

7500000000000000000

Ethereumはパブリックなブロックチェーンなので、ブラウザでもetherscanというサイトでもトランザクションを確認することができます。

スクリーンショット 2018-01-16 17.12.32.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)


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)


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)


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で確認してみます。

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

スクリーンショット 2018-01-16 17.30.48.png

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

続いて、Coinの情報を見てみます。

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

スクリーンショット 2018-01-16 17.29.35.png

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


4. まとめ

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

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

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

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