覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Ethereum - Ethereumクライアントのインストールと実行

Ethereum(イーサリアム)用のクライアントをPC(macOS)にインストールして実行してみます

Ethereumクライアントにはいくつか代表的なものが存在しています

  • go-ethereum (Golang)
  • cpp-ethereum (C++)
  • ethreumj (Java)
  • pyethapp (Python)
  • parity (Rust)

今回は一番一般的なGeth(go-ethreum)をインストールしてプライベートネットワークで実行してみます。
Geth の読み方は "ゲース" らしいです。


インストール

今回は、macOSなのでHomebrew でインストールします。
インストールするコマンドは以下になります

brew tap ethereum/ethereum
brew install ethereum


インストール後の確認

gethコマンドが実行できることを確認します。
今回は以下コマンド実行でバージョン情報を表示して確認します。

geth version

実行例

$ geth version
Geth
Version: 1.8.10-stable
Architecture: amd64
Protocol Versions: [63 62]
Network Id: 1
Go Version: go1.10.2
Operating System: darwin
GOPATH=/Users/tanuki/go
GOROOT=/usr/local/opt/go/libexec


プライベートネットでの実行

プライベートネットワークで動作させるために、ローカル環境に初めのブロックを作成します。
最初のブロックはGenesisブロックと呼ばれます。
Genesisブロックの定義を記述したGenesisファイル(以下に出てくる genesis.json)を用意します。

(実は、--dev のオプションを使うとこんな作業なしで楽できるみたいですが、今回はGenesisファイル用意してやってみます。)

ブロック保存ディレクトリを用意します。今回は private_eth という名称にします。適当な場所に作成します

genesis.json を 以下の内容として記述して、private_eth に保管します。

{
  "config": {
    "chainId": 77,
    "homesteadBlock": 0,
    "eip155Block": 0,
    "eip158Block": 0
  },
  "nonce": "0x0000000000000042",
  "timestamp": "0x0",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "gasLimit": "0x8000000",
  "difficulty": "0x100",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "coinbase": "0x3333333333333333333333333333333333333333",
  "alloc": {}
}


genesisブロックの初期化

以下コマンドを実行してGethの初期化処理を実行します

eth --datadir {ブロック保存ディレクトリのパス} init {Genesisファイルのパス}

実行例

$ geth --datadir /Users/yuki/work/private_eth init /Users/yuki/work/private_eth/genesis.json
INFO [08-11|16:33:59] Maximum peer count                       ETH=25 LES=0 total=25
INFO [08-11|16:33:59] Allocated cache and file handles         database=/Users/yuki/work/private_eth/geth/chaindata cache=16 handles=16
INFO [08-11|16:33:59] Writing custom genesis block 
INFO [08-11|16:33:59] Persisted trie from memory database      nodes=0 size=0.00B time=6.638µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [08-11|16:33:59] Successfully wrote genesis state         database=chaindata                                   hash=83b885…a305f3
INFO [08-11|16:33:59] Allocated cache and file handles         database=/Users/yuki/work/private_eth/geth/lightchaindata cache=16 handles=16
INFO [08-11|16:33:59] Writing custom genesis block 
INFO [08-11|16:33:59] Persisted trie from memory database      nodes=0 size=0.00B time=2.258µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [08-11|16:33:59] Successfully wrote genesis state         database=lightchaindata                                   hash=83b885…a305f3

以下の文字列が出力されれば成功です

INFO [YY-DD|HH:MM:SS] Successfully wrote genesis state


Gethの起動

初期化完了後に以下コマンドでGethを起動します

$ geth --networkid "10" --nodiscover --datadir {ブロック保存ディレクトリのパス} --rpc --rpcaddr "localhost" --rpcport "8545" --rpccorsdomain "*" --rpcapi "eth,net,web3,personal" --targetgaslimit "20000000" console 2>> {エラーログの出力先のパス}

実行例

$ geth --networkid "10" --nodiscover --datadir /Users/yuki/work/private_eth --rpc --rpcaddr "localhost" --rpcport "8545" --rpccorsdomain "*" --rpcapi "eth,net,web3,personal" --targetgaslimit "20000000" console 2>> /Users/yuki/work/private_eth/error.log
Welcome to the Geth JavaScript console!

instance: Geth/v1.8.10-stable/darwin-amd64/go1.10.2
 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

”Welcome to the Geth JavaScript console!” の文字列が出力されればGethの起動成功です


Geth起動後の操作

Geth起動後に、Gethコンソールでいくつかのアカウント操作を実行してみます


アカウントの作成

personal.newAccount を実行してアカウントを作成します(以下の "password123" の部分がパスワードで、任意の値を指定してください

> personal.newAccount("password123")
"0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e"

コマンドの実行結果として外部アカウントのアドレスが出力されます。この値は環境によって異なる値が出力されます。
パスワードの文字列はアカウントロックを解除するためのパスワードとなります。忘れないようにする必要があります。

試しに、後3アカウント作成してみます

> personal.newAccount("password123")
"0xbaa4290b7c18afe9905060e35b44ddd2a41b34e9"
> personal.newAccount("password123")
"0x559a14163bef0d310f489042707e4c73461e651a"
> personal.newAccount("password123")
"0x0e41e2a7b939ffa1b3d495ab43cda793939e41e5"


アカウントの確認

eth.accounts 実行で作成したアカウントを確認できます

> eth.accounts
["0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e", "0xbaa4290b7c18afe9905060e35b44ddd2a41b34e9", "0x559a14163bef0d310f489042707e4c73461e651a", "0x0e41e2a7b939ffa1b3d495ab43cda793939e41e5"]

作成したアカウントのアドレスが配列で表示されます。出力される配列のアドレスの個数は作成したアカウントの数と一致します。


インデックス指定でのアカウントの確認

eth.accounts でインデックス指定すると対象アカウントのアドレスを確認できます。インデックス0を指定して実行すると以下のようになります

> eth.accounts[0]
"0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e"


コインベースアカウントの確認

eth.coinbase 実行でコインベースアカウントを確認します。
コインベースアカウントとはブロック生成のマイニング報酬を受け取るアカウントです。
デフォルトではインデックス0のアカウントがコインベースアカウントになっています。

> eth.coinbase
"0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e"

結果がインデックス0のアカウントのアドレスと一致していることが分かります。


コインベースアカウントの変更

miner.setEtherbase でコインベースアカウントを変更できます。
今回は、インデックス2のアカウントに変更して eth.coinbase で変更後の内容を確認してみます

> miner.setEtherbase(eth.accounts[2])
true
> eth.coinbase
"0x559a14163bef0d310f489042707e4c73461e651a"

結果がインデックス2のアカウントのアドレスと一致していることが分かります。


コインベースアカウントをaccounts[0]に変更

一応、最後にコインベースアカウントをインデックス0のアカウントに戻しておきます。
やることは上記でやったことと基本的に同じです

> miner.setEtherbase(eth.accounts[0])
true
> eth.coinbase
"0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e"

コインベースアカウントが、インデックス0のアカウントに戻ったことがわかります


Gethの終了

Gethコンソールで ext を実行することがGethを終了させることができます

> exit
$ 


まとめ

というわけでGethの起動とアカウント操作までは試すことができました。



関連エントリ

Ethereum - ネットワークの種類

Ethereum(イーサリアム)では、用途に応じてネットワークが複数種類存在しています。

各ネットワークの特徴は以下の通りです。


メインネット

本番環境のネットワーク。全世界に公開されているパブリックな環境。
etherを取得するためにはマイニングもしくは取引所などで購入する必要がある。
マイニングのためにはメモリを大量に搭載したマシンが必要となるため、マイニングコストが非常に高い。

ライブネット や パブリックネット とも言われている(?)


テストネット

本番環境とは異なるが、広く認知されたステージング環境。全世界に公開されたパブリックな環境。
プライベートネットでのテストが終わった後に、本番環境にリリースする前の最終テストに利用するのが良い。
RopstenRinkeby と呼ばれるネットワークが存在している。
Ropsten に関してはether入手のためにはマイニングが必要になるがハードルが高いため、誰かから譲り受けるのが良い。


プライベートネット

アプリケーション開発時に利用するネットワーク。開発者が独自に構築でき、自分のローカル環境でも構築可能。
etherを入手するためにマイニングの必要はあるが、マイニングコストは低い。



関連エントリ

Go言語 - 開発支援ツール

Go言語での開発を行う際にお世話になる開発支援ツールがいくつか存在しています。
そこまで猛烈に意識しなくてもいい気もしますが、自分用のメモとしてコマンドラインでの入手方法等を記載しておきます


gofmt

gofmt は、Go標準で付属しているコードフォーマッターです。
ソースコードのインデント等を自動的に整形してくれます
好き勝手な独自スタイルが乱立しないように公式の単一のスタイルを強制してくれます。

使い方例(元のファイルを上書きする場合)

gofmt -w target.go


goimports

goimportsgofmt の上位互換のツールでコードフォーマット共に自動でimport文の挿入や削除を実施してくれます

入手方法

go get golang.org/x/tools/cmd/goimports


golint

Goとして望ましくないコーディングスタイルに対して警告をしてくれます。
ソースコードの問題報告というより、こういう風にした方がいいよというアドバイスをくれます

入手方法

go get -u golang.org/x/lint/golint


godoc

godoc は、強力なドキュメント閲覧ツールです。

入手方法

go get golang.org/x/tools/cmd/godoc

使い方(サンプル)

net/http について調べる場合

$ godoc net/http
use 'godoc cmd/net/http' for documentation on the net/http command 

PACKAGE DOCUMENTATION

package http
    import "net/http"

    Package http provides HTTP client and server implementations.

    Get, Head, Post, and PostForm make HTTP (or HTTPS) requests:

    resp, err := http.Get("http://example.com/")
    ...
    resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
    ...
    resp, err := http.PostForm("http://example.com/form",
        url.Values{"key": {"Value"}, "id": {"123"}})

    The client must close the response body when finished with it:`

(以下省略)


httpサーバとして利用する場合

godoc -http=:6070

のような実行をします

その後Webブラウザで http:localhost:6070 にアクセスすると以下のような画面が表示されます

f:id:nini_y:20180808005152p:plain`


gorename

gorename は、Goのリファクタリングツールです。変数名や関数名のリネーム等ができます

入手方法

go get -u golang.org/x/tools/cmd/gorename


guru

guru は、ソースコードの静的解析など行うツールです

入手方法

go get golang.org/x/tools/cmd/guru


gocode

コード補完を実施してくれるツールです

入手方法

go get -u github.com/nsf/gocode


godef

godef は、関数定義などへジャンプするためのツールです

入手方法

go get -u github.com/rogpeppe/godef


goreturns

goreturns は、importの追加や削除、フォーマット、function 内で戻り値に合わせた return の補完をしてくれます

入手方法

go get -u sourcegraph.com/sqs/goreturns

Go言語 - Windows上でのプロセス存在チェック

Windows上のプロセスの存在チェックをしたい場合のサンプルコードです。

前回記事のgo-psを利用すれば十分にチェック可能だと思いますが、今回のサンプルでは tasklist.exe に思いっきり頼っています。


package main

import (
    "bytes"
    "fmt"
    "os"
    "os/exec"
    "syscall"
)

func main() {
    if len(os.Args) < 2 {
        fmt.Printf("Parameter invalid")
        os.Exit(1)
    }
    args := os.Args
    targets := args[1:]
    isRunning, err := isWinProcRunning(targets)

    if err != nil {
        fmt.Printf("Commnand failed.")
        os.Exit(1)
    }

    fmt.Printf("Processes in (%v) any Running? -> %v", targets, isRunning)
}

func isWinProcRunning(names []string) (bool, error) {
    if len(names) == 0 {
        return false, nil
    }

    cmd := exec.Command("tasklist.exe", "/FI", "STATUS eq RUNNING", "/fo", "csv", "/nh")
    cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
    out, err := cmd.Output()
    if err != nil {
        return false, err
    }

    for _, name := range names {
        if bytes.Contains(out, []byte(name)) {
            return true, nil
        }
    }
    return false, nil
}


実行例 1

proccheck.exe chrome.exe
Processes in ([chrome.exe]) any Running? -> true


実行例 2

proccheck.exe chrome.exe RLogin.exe
Processes in ([chrome.exe RLogin.exe]) any Running? -> true


実行例 3

proccheck.exe Dummy1.exe Dumm2.exe Dummy3.exe
Processes in ([Dummy1.exe Dumm2.exe Dummy3.exe]) any Running? -> false


というわけで、tasklist.exe に頼れば、これだけではなく一覧出すことなんかも簡単にできると思います


関連エントリ

Go言語 - プロセスの情報出したり、プロセスの一覧出したり

安直なのかもしれないですが、go-psライブラリのお世話になると簡単です


以下で取得しましょう

go get github.com/mitchellh/go-ps


実行したプロセスの情報取得

実行したプログラムそのもののプロセス情報を取得する例です

package main

import (
    "fmt"
    "os"

    "github.com/mitchellh/go-ps"
)

func main() {
    currenProccessInfo()
}

func currenProccessInfo() {
    pid := os.Getpid()

    pidInfo, _ := ps.FindProcess(pid)

    fmt.Printf("%+v\n", pidInfo)

    fmt.Printf("> PID          : %d\n", pidInfo.Pid())
    fmt.Printf("> PPID         : %d\n", pidInfo.PPid())
    fmt.Printf("> Process name : %s\n", pidInfo.Executable())

    ppidInfo, _ := ps.FindProcess(pidInfo.PPid())
    fmt.Printf("> Parent process name : %s\n", ppidInfo.Executable())
}


実行結果例:

&{pid:37003 ppid:35930 binary:process1}
> PID          : 37003
> PPID         : 35930
> Process name : process1
> Parent process name : bash


実行中プロセスの一覧表示

実行中のプロセスの一覧を表示します。(面倒なので取得したプロセスの構造体の内容そのまま表示しています)

package main

import (
    "fmt"
    "os"

    "github.com/mitchellh/go-ps"
)

func main() {
    processList()
}

func processList() {
    processes, err := ps.Processes()

    if err != nil {
        os.Exit(1)
    }

    for i, p := range processes {
        fmt.Printf("%d : %+v\n", i, p)
    }
}


実行結果例:

0 ; &{pid:36453 ppid:35930 binary:process1}
1 ; &{pid:36412 ppid:28367 binary:Google Chrome He}
2 ; &{pid:36411 ppid:28367 binary:Google Chrome He}
3 ; &{pid:36410 ppid:28367 binary:Google Chrome He}
4 ; &{pid:36409 ppid:28367 binary:Google Chrome He}
5 ; &{pid:36407 ppid:28367 binary:Google Chrome He}
6 ; &{pid:36405 ppid:28367 binary:Google Chrome He}
7 ; &{pid:36265 ppid:1 binary:quicklookd}
8 ; &{pid:36126 ppid:28367 binary:Google Chrome He}
9 ; &{pid:36125 ppid:28367 binary:Google Chrome He}
10 ; &{pid:36123 ppid:28367 binary:Google Chrome He}
11 ; &{pid:36122 ppid:28367 binary:Google Chrome He}
12 ; &{pid:36109 ppid:28367 binary:Google Chrome He}
13 ; &{pid:36061 ppid:1 binary:LookupViewServic}
14 ; &{pid:36058 ppid:28367 binary:Google Chrome He}
15 ; &{pid:36057 ppid:28367 binary:Google Chrome He}
16 ; &{pid:36056 ppid:28367 binary:Google Chrome He}
17 ; &{pid:36055 ppid:28367 binary:Google Chrome He}
18 ; &{pid:36054 ppid:28367 binary:Google Chrome He}
19 ; &{pid:36053 ppid:28367 binary:Google Chrome He}
20 ; &{pid:36052 ppid:28367 binary:Google Chrome He}
21 ; &{pid:36051 ppid:28367 binary:Google Chrome He}
22 ; &{pid:36044 ppid:1 binary:gocode}
23 ; &{pid:36028 ppid:28367 binary:Google Chrome He}
24 ; &{pid:35930 ppid:35929 binary:bash}
25 ; &{pid:35929 ppid:35864 binary:Code Helper}
26 ; &{pid:35922 ppid:28367 binary:Google Chrome He}
(長いので中略)
330 ; &{pid:67 ppid:1 binary:iconservicesagen}
331 ; &{pid:66 ppid:1 binary:iconservicesd}
332 ; &{pid:61 ppid:1 binary:mds}
333 ; &{pid:60 ppid:1 binary:warmd}
334 ; &{pid:58 ppid:1 binary:airportd}
335 ; &{pid:54 ppid:1 binary:logd}
336 ; &{pid:49 ppid:1 binary:mobileassetd}
337 ; &{pid:48 ppid:1 binary:powerd}
338 ; &{pid:47 ppid:1 binary:configd}
339 ; &{pid:46 ppid:1 binary:appleeventsd}
340 ; &{pid:43 ppid:1 binary:mediaremoted}
341 ; &{pid:41 ppid:1 binary:fseventsd}
342 ; &{pid:40 ppid:1 binary:kextd}
343 ; &{pid:39 ppid:1 binary:uninstalld}
344 ; &{pid:37 ppid:1 binary:UserEventAgent}
345 ; &{pid:36 ppid:1 binary:syslogd}