覚えたら書く

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

Solidity - RemixでHello World

Solidityでのスマートコントラクト開発を行う上で、
統合開発環境としてはブラウザベースのRemixを利用するのが一番お手軽なようです。

とりあえずRemix を利用して Hello Wolrd を実行してみます。


Remixの起動

以下URLのアクセスします。

https://remix.ethereum.org

すると以下のような画面が表示されます

f:id:nini_y:20180813231913p:plain

ここで、左上の+ボタンをクリックします


ファイルの作成

FileNameに HelloWorld.sol と入力して「OK」をクリックします。

f:id:nini_y:20180813231920p:plain


コードの記述

以下のコードを記述します

pragma solidity ^0.4.23;

contract HelloWorld {
    
    function hello() public pure returns (string) {
        return "Hello World!!";
    }
}

HelloWorldというcontractに、"Hello World!!"という文字列をreturnするfunctionを定義します。
(constant修飾子をfunctionに付与する例が時々見られますが、現在はfunctionにconstantを付与するのは非推奨になっているようです)


コンパイル

画面右上の辺りにある「Start to compile」をクリックしてコンパイルを実行します。

f:id:nini_y:20180813232700p:plain


デプロイ

コンパイル完了後、「Run」タブをクリックします。

f:id:nini_y:20180813232839p:plain


以下の表示になるので、「Deploy」をクリックします

f:id:nini_y:20180813232936p:plain


functionの実行

「Deployed Contracts」の中のHello Worldのリストをクリックするとhelloというfunction名が表示されるので、
それをクリックします。

f:id:nini_y:20180813233006p:plain


するとfunctionの結果として "Hello World!!" の文字列が返されます。

f:id:nini_y:20180813233304p:plain


まとめ

Remixを利用することで、とりあえずSolidityのHello World を試すことができました。

Ethereum - トランザクションの実行の流れ

ビットコインのトランザクションはTXOモデルとなっています。
しかし、Ethereum(イーサリアム)ではアカウント情報を保存しているState Tree の中に残高情報を保持しているためトランザクションの実行フローが異なっています。


トランザクションが満たす必要がある条件

トランザクションは以下項目を満たす必要があります。

  • RLPフォーマット(入れ子構造のバイナリデータの符号化用データフォーマット)に従う形式で追加の後続バイトが存在しないこと。
  • 署名が有効であること。
  • nonce が有効であること。(Sate Treeのアカウントが保持するnonce と照合して有効であるかを確認する)
  • トランザクションに事前に定められたGas量 + 送信するデータを送信するのに必要となるGas量 よりも、Gas Limit が大きく設定されていること。
    コントラクト生成のトランザクションの場合は、さらに追加で 32,000 gas が必要となる。
  • 送信者アカウントの残高が上述のGas を上回っていること。

上記項目を満たす場合、トランザクションは次の処理に移り、サブ状態のSubstate を作成します。
Substate は以下の情報から構成される。

  • Self-Destruct Set
    • トランザクション完了後に破棄されるアカウント情報がある場合、ここに格納される。
  • Log Series
    • トランザクションがどこまで実行されたかを保持するチェックポイント。
      フロントエンドなどからトランザクションの実行状態を確認する場合に、この情報が必要となります。
  • Refund Balance
    • トランザクション完了後にアカウントに戻される金額。
      State Tree にデータを書き込むためにはGasが必要だが、逆にState Treeからデータを削除する場合、State Treeを小さくすることになり、
      State Tree を削減した報酬として金額が返ってくる仕組みとなっている。
      トランザクション実行の進捗状況に従って、状態データを削除していくと、本項目の値が増加する。
      実際には、トランザクション実行のコストと相殺されたあとに返却される。


トランザクションの実行

通常の送金やコントラクトを実行する "メッセージコールトランザクション" と コントラクトを生成する "コントラクト生成トランザクション" があり、
各々のトランザクションの実行ステップは以下のようになっています。


メッセージコールトランザクション

メッセージコールトランザクションの実行ステップは以下の通りです。

  1. State Tree にあるトランザクション送信者のnonceが1インクリメントされる
  2. トランザクションを実行するため、必要なGasがアカウントの残高から差し引かれる
  3. トランザクション実行後、残っているGasとRefund Balanceに記載されているGasから返金される金額を計算して返金する
    これによりトランザクションの状態が確定する
  4. Gasの返金受け取り後に、ブロックにトランザクションを取り込んだマイナー、もしくはブロック生成ノードに使用したGas分のETHが送られる
  5. Self-Destruct Set にアカウント情報が含まれていた場合、削除する
  6. トランザクションに使用したGas量とログを記録する


コントラクト生成トランザクション

コントラクト生成トランザクションの実行ステップは以下の通りです。
(メッセージコールトランザクションとは異なり、コントラクト生成トランザクションを生成したアカウントやEVMコードからサブ状態を作成し、その後に下記ステップでコントラクトアカウントを生成する。)

  1. トランザクションを発行したアカウントとnonceからコントラクトのアドレスを作成する
  2. nonceに0を指定する
  3. コントラクトアカウントにトランザクションに送金したEtherの金額を設定する
  4. アカウントのStorageを空に設定する
  5. codeHashに空の文字列ハッシュを設定する
  6. 初期化コードを実行してアカウントを作成する。
    初期化コードの実行はGasを消費する。Gasが不足した場合トランザクションは無かったものとなる
    Revert Codeでエラーが出力された場合、その時点で残っているGasが戻り、ステップ3で設定したEtherが戻る
  7. 初期化コードの実行に成功した場合、コントラクトのデータサイズに応じてGasが支払われ、余ったGasはトランザクションの送信者に戻る



関連エントリ・書籍

ブロックチェーンアプリケーション開発の教科書

ブロックチェーンアプリケーション開発の教科書

Ethereum - ブロック構造

Ethereumのブロックはヘッダとトランザクション、トランザクション実行結果の3要素で構成されています。

各要素の例と項目の説明をメモしておきます(初学者なので、以下の内容もどこまで正確なのかは不安あり・・・)


ブロックヘッダ

ブロックヘッダの例は以下の通りとなります

{
  difficulty: 174882,
  extraData: "0xd98301080a846765746888676f312e31302e328664617277696e",
  gasLimit: 67863675,
  gasUsed: 21000,
  hash: "0x369003471d86903a6527353318fcedb82ba400a551ceafabcf3c77e36a60c348",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e",
  mixHash: "0xbe2e1f8c000f003f0124798b144a6677787ea1476f552471f1f802bf87ce0120",
  nonce: "0x55cc0b5782b8812d",
  number: 698,
  parentHash: "0xbff2a4fdbbc229934bf13d486b4f8d9a167e466cd97749bc0249f7b339da03da",
  receiptsRoot: "0x05bccc39ceb3c0844b724a7606d8e7c40746f2517875cc974554fb03b3084d6c",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 655,
  stateRoot: "0x23367a17d56b419f3ec7584a7eeeade5538e63883cbf1ebd127cd18746c896aa",
  timestamp: 1534058867,
  totalDifficulty: 108836926,
  transactions: ["0x621ef85ea52fe43b547eea00809d20fced26df7873dc62c8e358fc6ed94a5c58"],
  transactionsRoot: "0xb109addf20eda9a6e5d6670b6e52038aa85282efd62ed1498fb927bea19497e1",
  uncles: []
}

ブロックヘッダの各項目は以下の通りです

  • difficulty
    • ブロックを生成する難易度。これ以前のブロックの難易度とタイムスタンプから算出されます。
  • extraData
    • ブロックに関連する任意の情報を記録する場所。サイズは32byte以下です。
  • gasLimit
    • このブロックで使用できるGasの最大サイズ。
  • gasUsed
    • このブロックで使われたGasの使用量。
  • hash
    • ブロックを表すハッシュ値。
  • logsBloom
    • ブロック内のトランザクションから出力されるログがブルームフィルタと呼ばれる形で記録されている。
      実行したアカウントの情報とそのトランザクションの内容、それに付随する情報が格納される。
      全トランザクションを確認しなくても、このブロックに特定のログがあるかどうかを判断されるために利用される。
  • miner
    • このブロックを生成し、採掘手数料を受け取るアカウントのアドレス(160bit)。
  • mixHash
    • このブロックで十分な量の計算が実行されたことを、nonceと組み合わせて証明する256bitのハッシュ値。
  • nonce
    • mixHash と組み合わせて、このブロックで十分な量の計算が実行されたことを表す。64bitのハッシュ値。
  • number
    • 現在のブロック番号。Genesisブロックの場合は値は0。
  • parentHash
    • 親ブロック(前のブロック)のヘッダのハッシュ値。KECCAK-256ハッシュ形式。
  • receiptsRoot
    • ブロックに入っているトランザクションの実行結果を保存している、データ構造のルートノードのハッシュ値。KECCAK-256ハッシュ形式。
  • sha3Uncles (ommersHash)
    • 現在のブロックのUncleブロック配列のハッシュ。KECCAK-256ハッシュ形式。
      Uncleブロックとはブロックが生成された時に同時期に生成されたブロックで、ブロック内のトランザクションは無視されるが生成の報酬をもらうべき対象のブロック。
  • size
    • このブロックのサイズ(単位:byte)
  • stateRoot
    • このブロックの全トランザクションが実行された状態のState Tree のルートノードのハッシュ値。KECCAK-256ハッシュ形式。
  • timestamp
    • ブロックがチェーンに取り込まれた時刻。Unixタイムスタンプ形式。
  • totalDifficulty
    • このブロック以前の難易度の総和。
  • transactions
    • ブロックに取り込まれているトランザクションのハッシュが配列として格納されている。
  • transactionsRoot
    • ブロックに入っているトランザクションを含んだ木構造のルートノードのハッシュ値。KECCAK-256ハッシュ形式。
  • uncles
    • Unclebブロックのハッシュ配列。


Ethereumはブロックヘッダに大まかに重要な情報が格納されています。
Ethereumのネットワークに接続する全クライアントが全ブロック情報を必要としているわけではありません。
送金やコントラクタの呼び出しを利用するクライアントは、ブロックのヘッダ情報のみをダウンロードし、
計算の際に必要に応じてヘッダ情報からブロックのトランザクション情報を取得する仕組みになっています。


トランザクション

トランザクションの例は以下の通りとなります

{
  blockHash: "0x369003471d86903a6527353318fcedb82ba400a551ceafabcf3c77e36a60c348",
  blockNumber: 698,
  from: "0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e",
  gas: 90000,
  gasPrice: 18000000000,
  hash: "0x621ef85ea52fe43b547eea00809d20fced26df7873dc62c8e358fc6ed94a5c58",
  input: "0x",
  nonce: 0,
  r: "0x50de58e50150a7325321814e09ef129b2bfca265721a077492fdbe485754e356",
  s: "0xfc01614f5150abe37fdeac2f22928dfc4200404f91370e4a4c882b7cb042ee2",
  to: "0x0e41e2a7b939ffa1b3d495ab43cda793939e41e5",
  transactionIndex: 0,
  v: "0xbd",
  value: 3000000000000000000
}

トランザクションの各項目は以下の通りです

  • blockHash
    • トランザクションがどのブロックに取り込まれているかを表す値。この値がnullの場合、トランザクションはブロックに取り込まれていない。
  • blockNumber
    • トランザクションが何番目のブロックに入っているかを表す。この値がnullの場合、トランザクションはブロックに取り込まれていない。
  • from
    • トランザクションを発行した送信者のアドレス。(20byteの値)
  • gas
    • 送信者が供給したGasの量。
  • gasPrice
    • トランザクションで払っても良いと決めたGasの金額。(単位:wei)
  • hash
    • トランザクションを表す32byteのハッシュ値。
  • input
    • トランザクションに送信されたデータ。
  • nonce
    • トランザクション以前に送信者が送信したトランザクションの数。
  • r
    • 送信者のトランザクションを特定する署名を作るために使用される。
  • s
    • 送信者のトランザクションを特定する署名を作るために使用される。
  • to
    • トランザクションを受け取った受信者のアドレス。(20byteの値)
  • transactionIndex
    • トランザクションのブロックの何番目のトランザクションとして入っているかを表す。この値がnullの場合、トランザクションはブロックに取り込まれていない。
  • v
    • 送信者のトランザクションを特定する署名を作るために使用される。
  • value
    • 送信者から受信者へ送る量。(単位:wei)


トランザクションの実行結果(レシート)

トランザクションの実行結果(レシート)を木構造で保存してるものをReceipt Tree と呼ぶ。
トランザクションレシートの内部構造の例は以下の通りです。

{
  blockHash: "0x369003471d86903a6527353318fcedb82ba400a551ceafabcf3c77e36a60c348",
  blockNumber: 698,
  contractAddress: null,
  cumulativeGasUsed: 21000,
  from: "0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e",
  gasUsed: 21000,
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  root: "0x0e999db3606fdfdcde919e7b05dde9947e9b7d54a22954d810519d687401f27b",
  to: "0x0e41e2a7b939ffa1b3d495ab43cda793939e41e5",
  transactionHash: "0x621ef85ea52fe43b547eea00809d20fced26df7873dc62c8e358fc6ed94a5c58",
  transactionIndex: 0
}

トランザクションレシートの各項目は以下の通りです

  • blockHash
    • トランザクションがどのブロックに入っているかを表す。
  • blockNumber
    • トランザクションが何番目のブロックに入っているかを表す。
  • contractAddress
    • コントラクトを作成するトランザクションの場合、コントラクトのアドレスが入る。
  • cumulativeGasUsed
    • トランザクション全体で使われたGasの使用量。例えば、他の処理もフックして実行された場合は、そのGas使用量も合算される。
  • from
    • トランザクションを発行した送信者のアドレス。(20byteの値)
  • gasUsed
    • トランザクションで使われたGasの使用量。
  • logs
    • トランザクションで生成されたログ。
  • logsBloom
    • ブロック内のトランザクションから出力されるログが、ブルームフィルタと呼ばれる形で記録される。
  • root
    • トランザクションがState Tree を変化させた後のState Root の値。
  • to
    • トランザクションを受け取った受信者のアドレス。(20byteの値)
  • transactionHash
    • 32byteのトランザクションハッシュ値。
  • transactionIndex
    • トランザクションがブロックの何番目のトランザクションとして入っているかを表している。



関連エントリ・書籍

ブロックチェーンアプリケーション開発の教科書

ブロックチェーンアプリケーション開発の教科書

Ethereum - Gethコンソールのコマンドを試す

Gethコンソールのコマンドをいくつか試してみます。

以下エントリの続きという感じになっています。

Gethを起動した状態で以下色々コマンドを実行しています。


ブロック内容の確認

eth.getBlock コマンドでブロックの内容を書くにすることができます。
引数に何番目のブロックを確認したいかのインデックスをしていします。
ブロックは、0→1→2→3... と積み上がっていき、ブロック高と呼ばれることがあります。
ブロック0がgenesisブロックに該当します。

実行例

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

genesisブロックの内容が表示されました


マイニングの開始と確認

miner.start コマンドでブロックを生成する毎に具を開始します。
引数には、マイニング処理を実行するスレッド数を指定します。

実行例

> miner.start(2)
null

nulltrueと表示されれば成功です。
が、いまいち本当に動作しているのかよくわからないので、eth.mining コマンドでマイニング中かどうかを確認します。
trueが表示されれば、マイニング中です。

実行例

> eth.mining
true


コインベースの残高確認

ブロックをマイニングのたびにetherが獲得でき、コインベースカウントの残高が増加していきます。
eth.getBalance コマンドで引数にコインベースアカウントのアドレスを指定します。
コインベースアカウントがaccounts[0]になっている前提で、以下実行しています。

実行例

> eth.getBalance(eth.accounts[0])
2.53e+21

上記の表示結果は単位がweiになっています。
これだとわかりにくい場合は、以下のようにweb3.fromWeiを利用して単位をetherに変換します。

実行例

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


マイニングの停止

マイニングを終了する場合は、miner.stop コマンドを実行します。trueと表示されればOKです。

> miner.stop()
true


送金

送金を行うためには、eth.sendTransaction コマンドを使用します。from に送金元アドレス、to に送金先アドレス、value に送金額を指定します。
ここでの総金額はweiの単位で指定する必要があり、ether単位の値を指定できないため、web3.toWeiを利用して単位をweiに変換します。
例えば accounts[0]からaccounts[3]に、3ether を送金する場合は以下のようになります

実行例

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[3], value: web3.toWei(3, "ether")})
Error: authentication needed: password or unlock
    at web3.js:3143:20
    at web3.js:6347:15
    at web3.js:5081:36
    at <anonymous>:1:1

しかし、コマンドの結果はエラーとなり、エラーメッセージが表示されました。
これは誤って送金してしまうのを防ぐために、accounts[0]がロックされているためです。ロックを解除する必要があります。


ロックの解除

ロックの解除には personal.unlockAccount コマンドを実行します。引数にはロック解除対象アカウントのアドレスを指定します。
accounts[0]のロックを解除する場合以下のようになります

実行例

> personal.unlockAccount(eth.accounts[0])
Unlock account 0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e
Passphrase: (ここでアカウント作成時に指定したpasswordを入力します)
true

true と表示されれば、ロックの解除成功です。


アンロックオプション付きの起動

本番環境であれば、アカウントをロックしておくのが望ましいでしょうがプライベートネットでは毎回パスワードを入力して、
ロックを解除するのは面倒なので、以下作業を実施してgeth起動時にアンロックしてしまうようにします。


gethの終了

作業のため一旦exit コマンドでgethを終了します

> exit


アカウントパスワードファイルの作成

password.txt というようなファイル名を作成し、ファイル内に以下のようにacctounts[0]→acctounts[1]→acctounts[2]→acctounts[3]の順に
改行区切りで各アカウントのパスワードを記述して保存します。

password123
password123
password123
password123


アンロックオプション付きでのGethの起動

以下のオプションを加えてGethを起動することで、最初からアカウントのロックを解除した状態にできます。

--unlock {アンロックするアカウントのアドレス} --password {パスワードファイルのパス}

アンロックするアカウントを複数指定する場合は、アドレスをカンマ区切りで指定します。

実行例

$ 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 --unlock 0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e,0xbaa4290b7c18afe9905060e35b44ddd2a41b34e9,0x559a14163bef0d310f489042707e4c73461e651a,0x0e41e2a7b939ffa1b3d495ab43cda793939e41e5 --password /Users/yuki/work/private_eth/password.txt
Welcome to the Geth JavaScript console!

instance: Geth/v1.8.10-stable/darwin-amd64/go1.10.2
coinbase: 0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e
at block: 697 (Sun, 12 Aug 2018 15:33:13 JST)
 datadir: /Users/yuki/work/private_eth
 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


改めて送金

一度エラーとなった送金処理を、アンロックオプションを指定して起動したGethで再度実施してみます。
(送信先のアカウントの残高を送金前に確認しておきます)

> web3.fromWei(eth.getBalance(eth.accounts[3]), "ether")
0
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[3], value: web3.toWei(3, "ether")})
"0x621ef85ea52fe43b547eea00809d20fced26df7873dc62c8e358fc6ed94a5c58"

トランザクションが成功した場合は16進数のトランザクションハッシュ値が出力されます。(トランザクションハッシュ値は毎回異なります)


トランザクションの確認

発行したトランザクションがどうなっているかを eth.getTransaction コマンドで確認することができます。
引数にはトランザクションハッシュ値を指定します。

実行例

> eth.getTransaction("0x621ef85ea52fe43b547eea00809d20fced26df7873dc62c8e358fc6ed94a5c58")
{
  blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
  blockNumber: null,
  from: "0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e",
  gas: 90000,
  gasPrice: 18000000000,
  hash: "0x621ef85ea52fe43b547eea00809d20fced26df7873dc62c8e358fc6ed94a5c58",
  input: "0x",
  nonce: 0,
  r: "0x50de58e50150a7325321814e09ef129b2bfca265721a077492fdbe485754e356",
  s: "0xfc01614f5150abe37fdeac2f22928dfc4200404f91370e4a4c882b7cb042ee2",
  to: "0x0e41e2a7b939ffa1b3d495ab43cda793939e41e5",
  transactionIndex: 0,
  v: "0xbd",
  value: 3000000000000000000
}

from にaccounts[0]のアドレスが、toにaccounts[3]のアドレスが表示されます。
blockNumber が null になっているので、このトランザクションはまだブロックに取り込まれていないことを意味しています。

ブロックに取り込まれていないトランザクションは確定していないため、送金はまだ完了していません。
この時点での送信先のアカウントの残高を確認してみると以下の通りです。

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

トランザクションを確定するためにはブロックを生成し、トランザクションがブロックに取り込まれる必要があります。
そのためにマイニングを再開します。

> miner.start(2)
null


マイニング後のトランザクション確認

一定時間後に改めて対象トランザクションの状態を確認してみます

> eth.getTransaction("0x621ef85ea52fe43b547eea00809d20fced26df7873dc62c8e358fc6ed94a5c58")
{
  blockHash: "0x369003471d86903a6527353318fcedb82ba400a551ceafabcf3c77e36a60c348",
  blockNumber: 698,
  from: "0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e",
  gas: 90000,
  gasPrice: 18000000000,
  hash: "0x621ef85ea52fe43b547eea00809d20fced26df7873dc62c8e358fc6ed94a5c58",
  input: "0x",
  nonce: 0,
  r: "0x50de58e50150a7325321814e09ef129b2bfca265721a077492fdbe485754e356",
  s: "0xfc01614f5150abe37fdeac2f22928dfc4200404f91370e4a4c882b7cb042ee2",
  to: "0x0e41e2a7b939ffa1b3d495ab43cda793939e41e5",
  transactionIndex: 0,
  v: "0xbd",
  value: 3000000000000000000
}

blockNumberにnullではなく数値が設定されています。この例の場合は、698番目のブロックに取り込まれていることを示しています。

この後に、送信先アカウントの残高確認すると予定通り送金が完了していることがわかります。

> web3.fromWei(eth.getBalance(eth.accounts[3]), "ether")
3


トランザクションレシートの確認

トランザクションが実行されるとレシートが発行されます。eth.getTransactionReceipt コマンドで確認できます

> eth.getTransactionReceipt("0x621ef85ea52fe43b547eea00809d20fced26df7873dc62c8e358fc6ed94a5c58")
{
  blockHash: "0x369003471d86903a6527353318fcedb82ba400a551ceafabcf3c77e36a60c348",
  blockNumber: 698,
  contractAddress: null,
  cumulativeGasUsed: 21000,
  from: "0xf7c80f38982e23707cad8a0ed0f78b0b65c5134e",
  gasUsed: 21000,
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  root: "0x0e999db3606fdfdcde919e7b05dde9947e9b7d54a22954d810519d687401f27b",
  to: "0x0e41e2a7b939ffa1b3d495ab43cda793939e41e5",
  transactionHash: "0x621ef85ea52fe43b547eea00809d20fced26df7873dc62c8e358fc6ed94a5c58",
  transactionIndex: 0
}

このレシート上からも、698番目のブロックに取り込まれていることが確認できます。


まとめ

というわけで、Gethでの送金含めたいくつかのコマンドを試してみました。



関連エントリ

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の起動とアカウント操作までは試すことができました。



関連エントリ