macでEthereumをテストネットでマイニングしてみた!初心者向け

 

本日の記事では、macを使ってイーサリアムテストネットに接続して、

 

マイニングしてイーサリアムをゲットしてみたいと思います。

 

Ethereum テストネットとメインネットの違いはこちらの記事を参考にしてください。

 

 

[st-minihukidashi fontawesome=”” fontsize=”” fontweight=”” bgcolor=”#FFB74D” color=”#fff” margin=”0 0 20px 0″ radius=”” add_boxstyle=””]ちなみに、この記事は下記の記事を参考にしてます。[/st-minihukidashi]

 

 

 

Mac OS へのGethのインストール

 

Homebrewを用いたインストールとソースからインストールする方法があります。

 

Homebrew を用いたGethのインストール

 

Homebrewをインストールがされてあれば、次のコマンドだけでインストールできます。

 

$ brew tap ethereum/ethereum
$ brew install ethereum

 

ソースからGethをビルドする

go-ethereumのレポジトリをクローン

 

$ git clone https://github.com/ethereum/go-ethereum

 

そして、次のコマンドを実行することでGethをビルドできます。(ビルドにはGoが必要になります)

 

$ cd go-ethereum
$ make geth

 

プライベート・ネットに接続する

Gethのインストールが完了したら早速Gethを起動します。

 

Ethereumでは以下の3つの形態のP2Pネットワークを構築し

 

ブロックチェーンを運用していくことが可能です。

  • パブリック・ネットワーク:不特定多数のノードのノードが参加し、かつその参加に制限が全くないネットワークです。参加ノードはそのネットワーク上で共有管理されたブロックチェーンに対して自由に、読み取り、トランザクションの発行、マイニングが可能です。仮想通貨としてのEthereumや、多くのパブリックなdAppはこのパブリックネットワーク上で運用されています。

  • コンソーシアム・ネットワーク:あらかじめ参加を許されたノードのみが参加することが可能なネットワークです。参加を許されるノードは一つの組織のみに管理されたものとは限らず、複数の利害関係が一致しない組織がそれぞれのノードを管理することが通常です。例えば国際送金の管理を行うブロックチェーンを構築したい場合、予め参加を許された複数の金融企業がそれぞれ管理するノードをこのP2Pのネットワークに参加することで、一つの企業にのみ管理されたシステムではない半非中央集権なシステムが構築可能になります。

  • プライベート・ネットワーク:一つの組織のみに管理されたノードのみが参加することが可能なネットワークです。ネットワークは自身の管理下に置くことが可能になり、中央集権的なP2Pシステムが可能になります。

ここでプライベート・ネットワークは、自分自身のみのネットワークなので容易にEtherの採掘が可能ですし、安全性も高いネットワークです。

そのため、Ethereumの動作を調べたり、分散型アプリケーション(Dapp)の開発作業など個人的な作業を行うには、プライベート・ネットワークを立ち上げてそこでいろいろ弄ってみると便利です。

そこで本節では、インストールしたGethを起動し、プライベート・ネットに接続するところまでを解説していきます。

Genesisファイルを作成する

Genesisファイルとは、ネットワークでやり取りされるブロックチェーンの最初(Block番号 “0”)のブロックであるGenesisブロックの情報を記述したファイルです。

プライベート・ネットでは独自のブロックチェーンをやり取りしていくため、独自のGenesisブロックを定義したGenesisファイルを用意して利用します。

まず任意の場所にプライベート・ネットのブロック情報やノード情報など各種データを格納するディレクトリ(データ・ディレクトリ)を作成します。

ここでは、ログイン・ユーザー(今回の例ではubuntu)のhomeディレクトリ直下に作成します。

% pwd
/Users/ryosuke-hujisawa/Desktop/eth_private_net

次に上記ディレクトリ内にjson形式の下記の内容 を記述したmyGenesis.jsonファイルを配置します。

{
  "config": {
    "chainId": 15
  },
  "nonce": "0x0000000000000042",
  "timestamp": "0x0",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "extraData": "",
  "gasLimit": "0x8000000",
  "difficulty": "0x4000",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "coinbase": "0x3333333333333333333333333333333333333333",
  "alloc": {}
}

Gethをプライベート・ネットで起動する

genesisブロックの初期化

 

データ・ディレクトリとgenesisファイルを作成したら、

以下のコマンドを実行しブロックチェーン情報をgenesisファイルの内容で初期化します。

% geth --datadir /Users/ryosuke-hujisawa/Desktop/eth_private_net init /Users/ryosuke-hujisawa/Desktop/eth_private_net/myGenesis.json
INFO [05-02|13:47:34.431] Maximum peer count                       ETH=50 LES=0 total=50
INFO [05-02|13:47:34.432] Set global gas cap                       cap=25000000
INFO [05-02|13:47:34.432] Allocated cache and file handles         database=/Users/ryosuke-hujisawa/Desktop/eth_private_net/geth/chaindata cache=16.00MiB handles=16
INFO [05-02|13:47:34.526] Writing custom genesis block 
INFO [05-02|13:47:34.527] Persisted trie from memory database      nodes=0 size=0.00B time="11.637µs" gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [05-02|13:47:34.527] Successfully wrote genesis state         database=chaindata hash="7b2e8b…7e0432"
INFO [05-02|13:47:34.527] Allocated cache and file handles         database=/Users/ryosuke-hujisawa/Desktop/eth_private_net/geth/lightchaindata cache=16.00MiB handles=16
INFO [05-02|13:47:34.619] Writing custom genesis block 
INFO [05-02|13:47:34.619] Persisted trie from memory database      nodes=0 size=0.00B time="3.212µs"  gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [05-02|13:47:34.620] Successfully wrote genesis state         database=lightchaindata hash="7b2e8b…7e0432"

本コマンドを実行すると、–datadirで指定したディレクトリ以下にディレクトリが新しく作成されて、

% ls
geth        keystore    myGenesis.json

その中にgenesisブロックのブロックチェーン情報が保存されます。ここで実行時に

WARN [02-04|09:03:55] No etherbase set and no accounts found as default

という警告が表示されますがこのノード(geth)でのデフォルトのウォレットのアドレスを作成していないために表示されるものです。これは次節で作成していくので、現時点では無視して構いません。

gethの起動

 

次に以下のコマンドを実行することでGethを起動します。

% geth --networkid "15" --nodiscover --datadir "/Users/ryosuke-hujisawa/Desktop/eth_private_net" console 2>> /Users/ryosuke-hujisawa/Desktop/eth_private_netgeth_err.log
Welcome to the Geth JavaScript console!

instance: Geth/v1.10.2-stable/darwin-amd64/go1.16.3
at block: 0 (Thu Jan 01 1970 09:00:00 GMT+0900 (JST))
 datadir: /Users/ryosuke-hujisawa/Desktop/eth_private_net
 modules: admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d
> 

ここで各オプションの意味は以下の通りです。

  • --datadir :本オプションはgethの動作時のブロックチェーンデータや各種ログの出力先を指定します。genesisブロックの初期化で指定したディレクトリと同一のものを指定してください。

  • --networkid "15" :本オプションで任意の正の整数のIDを指定することで、ライブ・ネットとは異なるネットワークを立ち上げることが可能です(ここでは15を指定)。genesisブロックの初期化で指定したchainidと同一の値を指定する必要があります。

  • --nodiscover :Gethはデフォルトで自動的に(同じネットワークID)のEthereumネットワークのノード(Peer)を探し接続を試みます。プライベート・ネットでは未知のノードとの接続を避けるため、このオプションで自動Peer探索機能を無効にします。

  • console:Gethには採掘やトランザクションの生成などを対話的に進めることができるコンソールが用意されています。consoleサブ・コマンドを指定することで、Gethの起動時に同時にコンソール立ち上げることが可能です。なお、consoleサブ・コマンドを付加せずに、Gethのプロセスをバックグラウンドで起動させておき、後からそのプロセスのコンソールを起動する事も可能です(下記TIP参照)。

上記コマンドを実行すると、下記の実行結果のように、幾つかの情報の表示の後に「>」のプロンプトが表示され、コンソールが起動されます。今後、特にことわりのない限りこのコマンドで起動したGethプロンプト上で作業していく前提で進めていきます。

Welcome to the Geth JavaScript console!

instance: Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9
 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ブロックがmyGenesis.jsonに記載されたものになっているのかを確認してみます。Gethプロンプト上で

> eth.getBlock(0)

のコマンドを実行してみます。このコマンドは指定したブロック番号のブロック情報を表示するもので、今回はブロック番号”0″を指定してGenesisブロックの情報を表示します。下の結果のように例えばdifficultyがmyGenesis.jsonに指定したものになっているはずです。(ただし16進表記から10進表記に変換されています。)

> eth.getBlock(0)
{
  difficulty: 16384,
  extraData: "0x",
  gasLimit: 134217728,
  gasUsed: 0,
  hash: "0x7b2e8be699df0d329cc74a99271ff7720e2875cd2c4dd0b419ec60d1fe7e0432",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0x3333333333333333333333333333333333333333",
  mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
  nonce: "0x0000000000000042",
  number: 0,
  parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
  receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 507,
  stateRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  timestamp: 0,
  totalDifficulty: 16384,
  transactions: [],
  transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  uncles: []
}
>

etherを採掘する

前節までで、Ethereumの代表的なクライアントであるGethをインストールし、テストネットへの接続、対話型のコンソールの立ち上げ方法を解説しました。

Ethereumは内部通貨etherが規定されており、Ethereumでトランザクションを発生させるためにはetherの手数料が必要になります。この節ではetherを採掘する手順を見ていきます。

アカウントの作成

Gethのコンソール上で新規のアカウントを作成します。Ethereumには2種類のアカウントが存在します。一つはEOA(Externally Owned Account)、もう一つはContractです。

EOAは私たちユーザーによりコントロールされるアカウントであり、我々ユーザーによる任意のタイミングで、EOAがトランザクションを発生させ、他のアカウントへのetherの送金、コントラクト・コードの実行などを行います。また、etherの採掘もこのEOAアカウントにより行われます。

一方Contractは一種の自動エージェントであり、EOAにより発生したトランザクションをトリガにContractアカウントが内部に持つコントラクト・コードが実行されます。それによりContractのフィールドのデータも更新されます。オブジェクト指向言語に馴染みのある人には、「Contractは一種のクラスのようなもので、EOAによりContractのメソッドが呼ばれ、そのメソッドが実行されるとContractの持つクラス変数が書き換えられる」というアナロジーでの説明が分かりやすいかもしれません。

ここでは、EOAを新規に作成していきます。 前節の手順に従い、Gethが起動されコンソールが表示された状態を前提とします。

まず、このノードに登録されたアカウント(EOA)を表示させてみましょう。eth.accountsコマンドはこのノード内で作成されたEOAのリストを表示するものです。現時点ではアカウントを作成していないため、下記のように実行しても空のリストが表示されるのみです。

> eth.accounts
[ ]

EOAの作成はpersonal.newAccount("passwd")コマンドで行います。ここでpasswdの部分は作成するEOAのパスワードです。実行時には適宜書き換えて実行してください。実行すると、作成されたEOAの20バイトのアドレスが表示されます。また、personal.listAccountsコマンドの実行結果にも作成したアカウントのアドレスが表示されるようになります。

> personal.newAccount("hogehoge01")
'0x24afe6c0c64821349bc1bfa73110512b33fa18e1'

>eth.accounts
['0x24afe6c0c64821349bc1bfa73110512b33fa18e1']

ここで、もう一つアカウントを作成しましょう(後で使用します)。

> personal.newAccount("hogehoge02")
'0x59c444d6c4f4187d1dd1875ad74a558a2a3e20b6'

> eth.accounts
['0x24afe6c0c64821349bc1bfa73110512b33fa18e1', '0x59c444d6c4f4187d1dd1875ad74a558a2a3e20b6' ]

【注意】パスワードを忘れると復元する手段はありません。絶対にパスワードは忘れないようにしてください。また、上記の例では簡易なパスワードを用いましたが、実際には、セキュリティの観点から半角英数記号を含む長い複雑なパスワードを設定するようにしてください。

etherbase

ここで、eth.coinbaseコマンドを実行してみます。すると下記のとおり実行結果には先ほど作成した2つのEOAのうちの一つが表示されます。このコマンドはetherbase(coinbaseとも呼ばれます)を表示するコマンドで、etherbaseとは、各ノードで採掘を行う際にその報酬を紐づけるEOAのアドレスを示します。

> eth.coinbase
'0x24afe6c0c64821349bc1bfa73110512b33fa18e1'

therbaseはデフォルトではプライマリーのアカウント(eth.accounts[0]コマンドを実行して表示されるアドレスのEOA)が設定されますが、下記のようにminer.setEtherbase(new_adress)コマンドで変更することも可能です。

> miner.setEtherbase(eth.accounts[1])
> eth.coinbase
'0x59c444d6c4f4187d1dd1875ad74a558a2a3e20b6'

etherの採掘

作成したEOAのアドレスがetherbaseとしてセットされていれば、etherの採掘が可能です。 etherの採掘はminer.start(thread_num)コマンドで開始します。ここでthread_num は採掘を何本のスレッドで同時実行するかを指定するパラメータです。指定しない場合は動作環境でのCPUコア数に設定されます。ここではthread_numは指定せず、以下のコマンドで採掘を開始します。

> miner.start()
null

また、採掘を停止したい場合は miner.stop()コマンドを実行すれば停止できます。

> miner.stop()
true

採掘状況の確認

採掘を開始するとブロックが次々と生み出されていきます。ブロックチェーンに何番目のブロックまで連なっているのか(=ブロック高)を確認するには、eth.blockNumberコマンドを用います。 採掘開始後しばらく すると、

> eth.blockNumber
145

といったように、ブロックが採掘されていることが確認できます。今回の結果の場合、145個のブロックが採掘されています。(今は自分だけが参加しているテストネットで採掘を行っているので、この145個のブロックは全て自分が採掘したことになります。)

採掘したブロックの内容を調べる

eth.getBlock(number)コマンドは、numberに任意のブロック高を指定すると、そのブロック高のブロック情報を表示することができます。 以下に、ブロック高が5と35のブロックの情報を表示してみます。

> eth.getBlock(5)
{
  difficulty: 131264,
  extraData: "0xd983010a02846765746888676f312e31362e338664617277696e",
  gasLimit: 133563653,
  gasUsed: 0,
  hash: "0x1a448c759938ed102f7f2f40e61028d5869f46bab004158666a78a309ec15ec3",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0xc7a437fe611c5dfb4cf237f6f2f898b637130c2d",
  mixHash: "0x7facc0ecc6ebae87013be04b15b6fc9483e5d6c1ce53ad151d9c3564b9527c0e",
  nonce: "0x532fbeff1d096710",
  number: 5,
  parentHash: "0x5f023b415e7aa1ece9db0068e7a85748ccf2ed027bd228fded6ac7dd25f6fc4e",
  receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 538,
  stateRoot: "0x5a201da6779f79388d3f3e9b1642c4892977a967d4f42b25ca6016d65991f189",
  timestamp: 1619932567,
  totalDifficulty: 672128,
  transactions: [],
  transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  uncles: []
}
> eth.getBlock(35)
{
  difficulty: 133185,
  extraData: "0xd983010a02846765746888676f312e31362e338664617277696e",
  gasLimit: 129705607,
  gasUsed: 0,
  hash: "0x1ec93a2ee5ccdfe53a867c4cd115b65f8662b61dbb18aeb3ed0b9d32f5414365",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0xc7a437fe611c5dfb4cf237f6f2f898b637130c2d",
  mixHash: "0xddfb8575256329d002c860d0c58d62e9761fa52a70ff380bf1488ed15e2b7cc7",
  nonce: "0x57fd7575ef06dcad",
  number: 35,
  parentHash: "0xee7cbf3c36b984ade23125f98d25f457d0afe82bb266522a7fdfc16a393a748c",
  receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 538,
  stateRoot: "0xe9c3c1fe6c175c881f360ff4e997e163b800e0d1a689d39174131dabc8145a9d",
  timestamp: 1619932597,
  totalDifficulty: 4639809,
  transactions: [],
  transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  uncles: []
}

ここでは、minernumberhashparentHashtransactionsの項目に着目します。

minerの項目はそのブロックを採掘したEOAのアドレスを示しています。今回は採掘者は自分一人の環境のテスト・ネットなので、採掘者は自分のEOAのアドレス(eth.coinbaseで得られるアドレス)が採掘者として記録されているはずです。

numberはそのブロックのブロック高を示しています。(今回はブロック高を指定して情報を表示しているので、指定したブロック高と同一の値が表示されているはずです。

hashはそのブロックのブロック・ヘッダ・ハッシュを表示しています。ブロック・ヘッダ情報をSHA-3アルゴリズム適用して得られた32-byteのハッシュ値です。このhashはブロックを指し示すユニークなIDとして利用されます。ここで、hashは、当該ブロックのデータ構造の中に含まれないことに注意してください。hashを知る必要がある場合には各自がブロック・ヘッダのデータを元に計算することになります。

parentHashは、親ブロックのブロック・ヘッダ・ハッシュを示しています。parentHashはブロック・ヘッダのデータ構造の中に含まれており、つまりは、子ブロックから親ブロックを参照していることになります。このような参照の連鎖が連なることでブロックのチェーン「ブロックチェーン」が形成されていることになります(下図参照)。

報酬の確認

採掘者が、自身の持つ計算資源を費やして採掘を行うインセンティブは、Ethereumの内部通貨であるetherを報酬として得られることにあります。実際に採掘をしたことによりetherが得られているかを確認してみましょう。

各アカウントのetherの持ち高を参照するにはeth.getBalance(address)コマンドを用います。持ち高を確認したいアカウントのアドレスをaddressパラメータに引き渡すことで確認が可能です。先の手順で、2つのアカウントを作成していました。それぞれのetherの持ち高を確認してみましょう。

> eth.accounts //作成したアカウントのアドレスを再確認
['0x24afe6c0c64821349bc1bfa73110512b33fa18e1', '0x59c444d6c4f4187d1dd1875ad74a558a2a3e20b6' ]
> 
> eth.coinbase == eth.accounts[0] //etherbaseは0番目のアカウントに紐づいている。
true
> eth.getBalance(eth.accounts[0])
'51500000000000000000'
> eth.getBalance(eth.accounts[1])
'0'

先に解説したとおり、coinbaseに紐づいたアカウントに採掘の報酬が与えられているのがわかります。eth.getBalance(address)は「wei」の単位 で持ち高が表示されます。以下の変換用のコマンドを使うことでetherの単位で表示することも可能です。

> web3.fromWei(eth.getBalance(eth.accounts[0]),"ether")
'515'

藤沢瞭介(Ryosuke Hujisawa)
  • りょすけと申します。18歳からプログラミングをはじめ、今はフロントエンドでReactを書いたり、AIの勉強を頑張っています。off.tokyoでは、ハイテクやガジェット、それからプログラミングに関する情報まで、エンジニアに役立つ情報を日々発信しています!

未整理記事

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です