【これからイーサリアムを学ぶ人のための教科書#4】イーサリアムの構成要素 Ethereum Virtual Machine、プルーフオブワーク

イーサリアム

【これからイーサリアムを学ぶ人のための教科書#4】イーサリアムの構成要素 Ethereum Virtual Machine、プルーフオブワーク

2019 年 5 月 - 9 min read

概要

  • 前回のおさらい
  • トランザクションの実行
  • トランザクションの実行モデル (EVM)
  • マイニングとプルーフオブワーク

連載「これからイーサリアムを学ぶ人のための教科書」ではこれからイーサリアムに関する情報を追っていくための基礎としてイーサリアムのプロトコルへの本質的な解説をしていきます。この連載を読み、技術レベルでの理解をしていただければ今後のイーサリアムに関する情報にキャッチアップしていくことができます。

前回のおさらい

前回はイーサリアムブロックチェーンの構成要素のうちトランザクションとブロックについて解説しました。前回をまだ読んでいない方はこちらから。

今回解説する要素は以下の通りです。

  • トランザクションの実行
  • 実行モデル (Ethreum Virtual Machine)
  • マイニング/プルーフオブワーク

今回も前回と同様、技術的に難しいと感じる部分に関しては漠然とした理解で読み進めて問題ありません。 

トランザクションの実行

ここではトランザクションを送信した際にイーサリアムブロックチェーンの状態にどういった変化が起こるのかを解説します。

トランザクションが実行されるためには以下のような条件を満たしておく必要があります。

  • トランザクションが正しくフォーマットされている (RLP, recursive length prefixというフォーマット)
  • 有効なトランザクション署名
  • 有効なトランザクションのナンス
    • 送信者のアカウントのナンス (送信者がとトランザクションのナンスが一致する必要がある
  • 十分なガスリミット
    • 設定されたトランザクションのガスリミットがトランザクションで消費されるの目安であるintrinsicガスを上回っている必要がある
  • 送信者のアカウントの残高がupfrontガスコストを上回っている
    • 設定したガスの最大値以上のEtherを持っている必要がある

上記のIntrinsicガスは以下のように計算されます。

送信するトランザクションのコードのバイト数が大きいほど必要なガスリミットが大きくなることがわかります。

上記の条件を満たしたトランザクションのみが実行されます。

トランザクションの実行のプロセスを説明していきます。

まずはじめに送信者のアカウント残高からupfrontガスコストが差し引かれ、送信者のアカウントの、nonceが1増加します。ここで設定したガスリミットから実際に消費されたガスを引いたものが残ガスになります。

次にトランザクションが実行されます。

トランザクションの実行を通してイーサリアムはsubstateというトランザクションが終了した時点で必要になる情報を記録します。substateは以下の要素によって構成されています。

  • Self-destruct set:トランザクションが終了した時に破棄されるアカウントの情報
  • Log series:仮想マシン (EVM) のコード実行のチェックポイント
  • Refund balance:トランザクションの送信者に返金される額 (返金に関しては前回のストレージの手数料の欄を参照) 

トランザクション実行のプロセスでは様々な計算が行われます。(数式などは割愛します。)

全てのプロセスが実行され、無効にならずに完了すると、残ガスと上記のrefund balanceが送信者のアカウントに返金され、ファイナライズ (後述)されます。

送信者への返金が完了すると、

  • マイナーにガス分のEtherが送られ、
  • 今回のトランザクションで使われたガスが、ブロックガスカウンター (ブロック内の全てのトランザクションで使われたガスの合計) に加えられ、
  • 上記のself-destruct setで指定されたアカウントが破棄されます。

以上で全てのプロセスは完了し、トランザクションによって発生したログと共にイーサリアムのステートが新しいステートに移行します。

以上がトランザクションの実行の基本的なプロセスですが、2種類の異なるトランザクション (コントラクト生成、メッセージコール) ではどのように異なるのか解説していきます。

コントラクト生成

前回、イーサリアムには以下の2種類のアカウントが存在すると説明しました。

  • コントラクトアカウント
  • 外部所有アカウント (EOA)

コントラクト生成トランザクションは新たに上記のコントラクトアカウントを生成するトランザクションという意味になります。

新しいコントラクトを生成するためにはまず、特殊な数式に基づいて新しいアカウントのアドレスが決定され、以下のプロセスが実行されます。

  • アカウントのnonceを0に設定する
  • アカウント生成トランザクションでEtherが同時に送られていた場合、アカウント残高をその額に設定する
  • アカウント生成トランザクションの送信者のアカウント残高からその額のEtherが差し引かれる
  • ストレージを空に設定する
  • コントラクトのcodeHashを空の文字列のハッシュに設定する

以上のプロセスが実行された後、init codeが実行され、アカウントが開始されます。(上記のトランザクションの構成要素を参照) init codeが実行される際に行われることはコントラクトの作成者によって異なります。

init codeが実行されアカウントが開始する際にはガスが消費されます。この時消費されるガスがアカウント生成トランザクションの残ガスを上回っている場合、ガス切れとなってエラーが発生し、トランザクション発生前の状態に戻ってしまいます。前回の記事で説明したのように、この場合、消費されたガスは返金されません。

しかし、アカウント生成トランザクションで同時にEtherが送られていた場合、そのEtherは返金されます。

init codeが実行され、アカウントの開始に成功した場合、コントラクト生成コストが支払われます。これは、ストレージ手数料のことで、コントラクトのコードの大きさに比例して増加します。ここでも十分な残ガスがなければエラーが発生し、処理は中止されます。

エラーが発生することなく、アカウントが生成された場合、最終的な残ガスがコントラクト生成トランザクション送信者のアカウントに返金され、ステートが移行します。

メッセージコール

メッセージコールのトランザクションはコントラクト生成トランザクションと少し異なる点があります。

メッセージコールの場合、新たなアカウントを生成しないためinit code含まれません。その代わり、inputデータという、EVM (後述) に行わせる処理の内容が含まれます。そして、実行された後、outputデータとしてinputデータで指示した処理の内容の結果を保持するようになります。このoutputデータは、以降のトランザクションが実行されるときに必要とされます。

コントラクト生成トランザクションと同じように、メッセージコールのトランザクションでもガス欠になった場合はトランザクションは無効となります。

以前、イーサリアムでは設定したガスリミットに達するまでトランザクションを中止する方法はありませんでしたが、現在はrevertコードによって設定したガスリミットに達する前にトランザクションを終了させることができます。

トランザクションの実行モデル (EVM)

トランザクションの実行の欄ではトランザクションの開始から終了までのプロセスを説明しましたが、この欄では仮想マシン (VM) の中で実際にどのようにトランザクションが実行されているかを解説していきます。

この項目はイーサリアムの構成要素の中で最も複雑で理解が難しいパートです。したがってEVMというものがどのようなものか、イメージを掴むことができれば問題ありません。

実際にトランザクションを実行するのはEthreuem Virtual Machine (以下EVM) という仮想マシンです。

EVMは前回の記事で説明した通り、チューリング完全な仮想マシンです。通常のチューリング完全な機械とは異なり、EVMにはガスという制限があります。したがって、支払われるガスの量によって実行できる計算量が決定されます。

さらに、EVMはスタックマシンです。スタックマシンとはスタックという構造に基づいた機械で後入れ先出し (last in first out) の構造でデータを保持します。

各スタックのサイズは256bitとなっており、スタックサイズの最大値は1024となっています。

EVMにはデータを文字列として保存するメモリが存在しますが、このメモリは揮発性があり、永続的ではありません。

メモリに加えてEVMにはストレージも存在します。メモリとは異なり、揮発性はなくステートの一部として維持され続けます。EVMではコードはストレージとは別に特別な指示によってのみアクセスできる仮想ROMに保存されます。

EVMはEVM bytecodeという独自の言語も持っています。イーサリアムで動作するスマートコントラクトプログラムを書くときには通常、solidity (EVM独自の高級言語) のような高級言語を用い、EVM bytecode にコンパイルします。

計算を実行する前にプロセッサによって以下の情報が有効であるか確認されます。

  • システムのステート
  • 計算のための残ガス
  • 実行するコードを持つアカウントのアドレス
  • 実行するトランザクションの送信者のアドレス
  • コードを実行されたアカウントのアドレス (トランザクションの送信者と異なる場合がある、インターナルトランザクションなど)
  • 実行したトランザクションのガスプライス
  • 実行するinputデータ
  • この実行の一部としてアカウントに送られるWeiの数
  • 実行されるマシンコード
  • 現在のブロックのブロックヘッダー
  • メッセージコールまたはコントラクト生成スタックの深さ

計算が実行される段階ではメモリとスタックは空であり、プログラムカウンターは0となります。

EVMはトランザクションを再帰的に実行し、ワールドステートとマシンステートを計算します。

マシンステートは以下によって構成されています。

  • 利用可能なガス
  • プログラムカウンター
  • メモリの中身
  • メモリ内のアクティブな文字数
  • スタックの中身

再帰的な実行のサイクルごとに、適切な額のガスが残ガスから差し引かれ、プログラムカウンターが増加します。

各サイクルには以下の3つの場合があります。

  • マシンがガス欠などにより例外的な状態となり、停止する
  • 次のサイクルまで問題なく進む
  • 実行プロセスが終了し、マシンが停止する

例外的な停止にならず、実行プロセスの終了とともにマシンが停止した場合、結果のステート、残ガス、substate、そしてoutputを生成します。

ここまでがEVMとトランザクションモデルの解説となりますが、この項目はとても複雑です。したがって、ここまでの深いレベルでの理解は必要でありません。大まかなイメージをつかんで頂ければ良いと思います。

ブロックのファイナライズ (最終決定)

ブロックのファイナライズという言葉には二つの意味があり、既存のブロックをファイナライズするか新しいブロックをファイナライズするかによって意味が異なります。

既存のブロックの場合、ブロックをvalidate (有効化) することを指し、新しいブロックの場合マイニングをするためのプロセスを指します。

いずれの場合もブロックがファイナライズされるためには以下の4つのプロセスを踏みます。

1.

-既ブロック:ommerを有効化

-新ブロック:ommerを決定

ブロックヘッダーの各ommerブロックは有効なヘッダーであり、現在のブロックから6世代前以内であることが確認される。

2.

-既ブロック:トランザクションを有効化

-新ブロック:トランザクションを決定

gasUsedの数字がブロック内のトランザクションで消費されたガスの合計と一致していることが確認される。

3.

報酬を適用する (新ブロックの場合)

beneficiaryで指定されたアカウント(基本的にマイナーのアカウント)に報酬として3ETHが与えられる。ommerブロックのマイニングの報酬に関しては第2回を参照

4.

既ブロック:ステートとnonceの検証

新ブロック:有効なステートとnonceが計算される

全てのトランザクションとそれによる結果のステートの変化が適用されていることが確認され、ブロックがファイナライズ (新ブロックの場合生成) される。

マイニングとプルーフオブワーク

第1回と第3回でマイニングとブロックの難易度に関しては大まかに解説しました。この項目では、それを成り立たせているプルーフオブワーク (Proof of Work, PoW) について解説します。

イーサリアムのプルーフオブワークのアルゴリズムはEthashと呼ばれており、以下の数式によって定義されます。

ここでmはmixHash、nはnonce、Hn (斜線の方) は新しいブロックのヘッダー、Hnはブロックヘッダーのnonce、dは大きなデータ群を表すDAGとなっています。

mixHashとnonceは前回のブロックの項目で解説しましたが、もう一度説明します。

  • mixHash:nonceと組み合わせることで、ブロックのために十分な計算がされたことを証明するハッシュ
  • nonce:mixHashと組み合わせることで、ブロックのために十分な計算がされたことを証明するハッシュ

上記の数の計算方法は複雑ですので、ここでは割愛します。

マイニングというセキュリティのメカニズム

PoWというアルゴリズムの目的はイーサリアムを暗号学的に安全であると証明することです。

このアルゴリズムではnonce値を計算し暗号を解読するためには総当たり方式しか存在しないため、悪意のある攻撃者が悪意のあるブロック生成するためには多大な計算リソースが必要になります。イーサリアムネットワーク全体の計算リソースのうち51%を上回る必要があり、51%以上の計算リソースを以って攻撃することを51%攻撃と呼びます。

このPoWというアルゴリズムは電力消費やスケーラビリティの問題など様々を内包しています。そのため、イーサリアムではプルーフオブステークという別のアルゴリズムに移行することが予定されています。プルーフオブステークに関しては次回以降に解説します。

以上がイーサリアムの構成要素であるトランザクションの実行モデルとマイニングの解説になります。

ここまで第1回から第4回ではイーサリアムブロックチェーンの外観と各構成要素にフォーカスして解説してきました。今回でイーサリアムブロックチェーンの基本的な説明は終了となります。

しかし、イーサリアムも様々な問題を抱えており、それに対する解決策が日々模索されています。次回以降はその解決策である、プルーフオブステーク (Casper FFG, CBC)、シャーディング、プラズマなどを解説していきます。どうぞお楽しみに。


参考