最近毎日のようにテレビや新聞に取り上げられている仮想通貨ですが、ちょっと試してみたいなぁと思っても実際に投資するのは怖いし・・・って感じだったので、Geth(Go Ethereum)を使って、イーサリアムのプライベートネットを構築し、送金を行ってみました。
環境構築
あまりローカル環境を汚したくなかったので、Docker で環境を構築しました
まずは、最新の golang のイメージでコンテナを立ち上げ、コンテナの中に入ります。
$ docker run -it --name geth golang:1.9 /bin/bash
ここからは Docker のコンテナ内での作業となります。
Geth(Go Ethereum)を git clone
し、ソースからビルドします。
ビルドしたら、Geth のバイナリファイルが置かれる場所にパスを通しておきます。
# git clone -b release/1.8 https://github.com/ethereum/go-ethereum ~/go-ethereum # cd ~/go-ethereum/ # make geth # export PATH=/root/go-ethereum/build/bin:$PATH
パスを通したら、ちゃんとインストールできているか、バージョンを表示してみます。
# geth version Geth Version: 1.8.1-unstable Git Commit: 9fd76e33af367752160ab0e33d1097e1e9aff6e4 Architecture: amd64 Protocol Versions: [63 62] Network Id: 1 Go Version: go1.10 Operating System: linux GOPATH=/go GOROOT=/usr/local/go
バージョンが表示され、Geth が問題なくインストールされたのが確認できました!
Ethereum のプライベートネットを構築する
genesis.json
の作成
Geth のインストールが完了したので、いよいよプライベートネットを構築したいと思います。
まずは作業用のディレクトリを作成します。
# mkdir ~/geth
次に作業用のディレクトリ直下に genesis.json
を作成します。
genesis.json
は Genesis ブロックと呼ばれる、最初のブロックを作成するためのファイルです。
# cat << EOS > ~/geth/genesis.json { "config": { "chainId": 12345, "homesteadBlock": 0, "eip155Block": 0, "eip158Block": 0 }, "timestamp": "0x0", "gasLimit": "0x8000000", "difficulty": "0x400", "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x3333333333333333333333333333333333333333", "alloc": {} } EOS
プライベートネットの初期化処理
genesis.json
を作成したら、そのファイルを元に、プライベートネットの初期化を行います。
このとき、データ格納用のディレクトリを指定する必要があるので、作業用に作成したディレクトリを指定しておきます。
# geth --datadir ~/geth/ init ~/geth/genesis.json INFO [02-18|04:48:44] Maximum peer count ETH=25 LES=0 total=25 INFO [02-18|04:48:44] Allocated cache and file handles database=/root/geth/geth/chaindata cache=16 handles=16 INFO [02-18|04:48:44] Writing custom genesis block INFO [02-18|04:48:44] Persisted trie from memory database nodes=0 size=0.00B time=8.896µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B INFO [02-18|04:48:44] Successfully wrote genesis state database=chaindata hash=7c0bc2…bb4ff6 INFO [02-18|04:48:44] Allocated cache and file handles database=/root/geth/geth/lightchaindata cache=16 handles=16 INFO [02-18|04:48:44] Writing custom genesis block INFO [02-18|04:48:44] Persisted trie from memory database nodes=0 size=0.00B time=11.494µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B INFO [02-18|04:48:44] Successfully wrote genesis state database=lightchaindata hash=7c0bc2…bb4ff6
Successfully wrote genesis state
という出力が表示されたら初期化は完了です。
プライベートネットの起動
プライベートネットの初期化が完了したので、プライベートネットを起動し、コンソールを立ち上げます。
# geth --networkid 10 --datadir ~/geth/ console 2>> ~/geth/error.log Welcome to the Geth JavaScript console! instance: Geth/v1.8.1-unstable-9fd76e33/linux-amd64/go1.10 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0 >
これでプライベートネットが起動し、コンソール上からコマンドを実行できる状態になりました。
Genesis ブロックの確認
まずはプライベートネットの初期化処理の時に指定した genesis.json
によって作成された Genesis ブロックを確認してみます。
ブロックの確認には eth.getBlock()
コマンドを使用します。
第一引数にはブロック番号を指定し、Genesis ブロックは最初のブロックなので 0
を指定することによって、Genesis ブロックを確認することができます。
> eth.getBlock(0) { difficulty: 1024, extraData: "0x", gasLimit: 134217728, gasUsed: 0, hash: "0x7c0bc26e9b9b9135eb6dd41b9f0bc8edac47998cd2e30720b3f1497b12bb4ff6", logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", miner: "0x3333333333333333333333333333333333333333", mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000", nonce: "0x0000000000000000", number: 0, parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000", receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", size: 507, stateRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", timestamp: 0, totalDifficulty: 1024, transactions: [], transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", uncles: [] }
まだマイニングを開始していないので新しいブロックは作られておらず、引数に 1
を指定すると null
が返ってきます。
> eth.getBlock(1) null
アカウントの作成
まだプライベートネットを起動したばかりの状態で、アカウントが存在しないので、まずはアカウントを作成してみましょう。
personal.newAccount()
コマンドの第一引数にパスワードを文字列で指定することによって、新しくアカウントが作成されます。
> personal.newAccount("password") "0x27963c263a5c42f84b169a1aff9d4411c2d666fa"
これでアカウントが1つ作成されました。
この後に送金を試していくので、これをあと4回繰り返して、合計5つのアカウントを作成しておきましょう。
アカウントの確認
作成されたアカウントを eth.accounts
コマンドで確認してみます。
> eth.accounts ["0x27963c263a5c42f84b169a1aff9d4411c2d666fa", "0x81bfeed39ebdd70b99a82e34a5d78a4388221ad2", "0xd6e5d2876988a5d3570254dd0c6e6aac87784df6", "0xaacf137f7d307ae43ca183693f51311166f22802", "0x2c102c2e294d131a698c37e54183b9c0ccaaee03"]
合計5つのアカウントが作成され、配列形式で表示されているのが確認できるかと思います。
配列形式なので、インデックスを指定することによって、それぞれのアカウントにアクセスすることもできます。
> eth.accounts[0] "0x27963c263a5c42f84b169a1aff9d4411c2d666fa"
コインベースアカウントの確認・変更
コインベースアカウント(マイニングをした時に報酬が支払われるアカウント)はデフォルトでは accounts[0]
に設定されています。
> eth.coinbase "0x27963c263a5c42f84b169a1aff9d4411c2d666fa"
ここではコインベースアカウントを accounts[0]
から accounts[1]
に変更します。
> miner.setEtherbase(eth.accounts[1]) true > eth.coinbase "0x81bfeed39ebdd70b99a82e34a5d78a4388221ad2"
マイニングの開始
コインベースアカウントを accounts[1]
に変更したので、マイニンングを開始すると accounts[1]
に報酬が支払われていきます。
> miner.start(1) null
これでマイニングが開始されました。
今マイニングが開始されているかどうかは eth.mining
コマンドで確認できます。
> eth.mining true
少し時間を置いて、ある程度マイニングが進んだらマイニングの停止してみましょう。
> miner.stop() true
マイニングを停止した状態の場合、eth.mining
コマンドを実行すると false
が返ってきます。
> eth.mining false
コインベースアカウントの残高確認
ある程度マイニングが進み、コインベースアカウントは報酬をもらっているはずなので、残高を確認してみます。
> eth.getBalance(eth.accounts[1]) 210000000000000000000
残高が表示されましたが、桁数が多すぎてかなり見づらいです。
これは残高の単位が wei
で表示されているからなので、単位を wei
から ether
に変換して表示してみます。
> web3.fromWei(eth.getBalance(eth.accounts[1]), "ether") 210
コインベースアカウントの残高が 210 ether あるのが確認できました。
ロックの解除
報酬を得たので、他のアカウントに送金をしてみたいところなのですが、そのまま送金をしようとするとエラーになってしまいます。
これは誤操作によって間違った送金を防ぐためのようで、送金する際にはアカウントを作成したときのパスワードでロックを解除してあげなくてはいけません。
> personal.unlockAccount(eth.accounts[1]) Unlock account 0x81bfeed39ebdd70b99a82e34a5d78a4388221ad2 Passphrase: true
送金
アカウントのロックが解除できたので、accounts[1]
から eth.accounts[0]
への送金を試してみたいと思います。
まずは送金先の eth.accounts[0]
の残高が 0
であることを確認します。
> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether") 0
次に、実際に accounts[1]
から accounts[0]
に 10 ether 送金してみます。
> eth.sendTransaction({from: eth.accounts[1], to: eth.accounts[0], value: web3.toWei(10, "ether")}) "0x72baef0e9c1f12ae7f625fe4838bdd0e9cca01010f7311a304ea994efb883731"
送金する単位は wei
なので 10 ether を wei
に変換しています。
ロックをかける
送金が終了したら、アカウントのロックを掛け直しておきます。
> personal.lockAccount(eth.accounts[1]) true
トランザクションを確認
eth.sendTransaction()
コメンドを実行して送金した際に返り値として、トランザクションハッシュ値が返ってくるので、トランザクションを確認してみます。
> eth.getTransaction("0x72baef0e9c1f12ae7f625fe4838bdd0e9cca01010f7311a304ea994efb883731") { blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", blockNumber: null, from: "0x81bfeed39ebdd70b99a82e34a5d78a4388221ad2", gas: 90000, gasPrice: 18000000000, hash: "0x72baef0e9c1f12ae7f625fe4838bdd0e9cca01010f7311a304ea994efb883731", input: "0x", nonce: 0, r: "0xb8ea73f8b8057b75ace72b26ee4356d2d405d610e30df9d8975d921bdd49c37f", s: "0x1c66eeb82edd12aa2b494b8370e1d0b97d9d821e3a549f1add26cbee9f951a54", to: "0x27963c263a5c42f84b169a1aff9d4411c2d666fa", transactionIndex: 0, v: "0x6095", value: 10000000000000000000 }
マイニングを停止しているので、このトランザクションはまだブロックに取り込まれておらず、送金はまだ完了していないです。
マイニングを再開して、トランザクションがブロックに取り込まれると、blockNumber
の値が null
から、取り込まれたブロックの番号に変更されます。
トランザクションをブロックに取り込む
それでは、マイニングを再開して、先ほどのトランザクションをブロックに取り込んでみましょう。
> miner.start(1)
少し時間を置いてからトランザクションを確認してみます。
> eth.getTransaction("0x72baef0e9c1f12ae7f625fe4838bdd0e9cca01010f7311a304ea994efb883731") { blockHash: "0xea90ca8cd6934ee9fd950547c8008b93a0894690e7e2d30363ae9186614c3fd3", blockNumber: 53, from: "0x81bfeed39ebdd70b99a82e34a5d78a4388221ad2", gas: 90000, gasPrice: 18000000000, hash: "0x72baef0e9c1f12ae7f625fe4838bdd0e9cca01010f7311a304ea994efb883731", input: "0x", nonce: 0, r: "0xb8ea73f8b8057b75ace72b26ee4356d2d405d610e30df9d8975d921bdd49c37f", s: "0x1c66eeb82edd12aa2b494b8370e1d0b97d9d821e3a549f1add26cbee9f951a54", to: "0x27963c263a5c42f84b169a1aff9d4411c2d666fa", transactionIndex: 0, v: "0x6095", value: 10000000000000000000 }
blockNumber
が null
から 53
に変更されました!
トランザクションがブロックに取り込まれ、送金が完了したので、accounts[0]
の残高を見ると、10 ether になっているのが確認できます。
> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether") 10
実は今まで残高を確認してきた eth.getBalance()
コマンドの第二引数に integer を渡すと、そのブロックの時点での残高を見ることもできます。
> web3.fromWei(eth.getBalance(eth.accounts[0], 52), "ether") 0 > web3.fromWei(eth.getBalance(eth.accounts[0], 53), "ether") 10
コンソールを終了する
コンソールの終了は exit
コマンドで出来ます。
> exit
まとめ
Docker のコンテナの中にイーサリアムのプライベートネットを構築し、実際に送金まで行ってみました。
環境の構築自体も Docker を使わなくても、インストーラーが準備されていたり、Mac の方は Homebrew でも環境を構築することができるようです。
ちょっと試してみたいけど、実際に投資するのはイヤだなぁと思っている方は是非プライベートネットを構築し色々と試してみてください。
追記
- 2018/3/1:Docker を立ち上げる時と、git clone する時のバージョンを固定しました
堅牢なスマートコントラクト開発のためのブロックチェーン[技術]入門
- 作者: 田篭照博
- 出版社/メーカー: 技術評論社
- 発売日: 2017/10/27
- メディア: 大型本
- この商品を含むブログを見る
ブロックチェーン・プログラミング 仮想通貨入門 (KS情報科学専門書)
- 作者: 山崎重一郎,安土茂亨,田中俊太郎
- 出版社/メーカー: 講談社
- 発売日: 2017/12/29
- メディア: Kindle版
- この商品を含むブログを見る