Skip to content
This repository has been archived by the owner on May 26, 2022. It is now read-only.

[Japanese] Design Rationale

Joseph Cook edited this page May 24, 2022 · 8 revisions

🛑 This wiki has now been deprecated. Please visit ethereum.org for up-to-date information on Ethereum. 🛑

Contents

設計原理 English version

Ethereum が既に試されテストされてきた5年前からあるビットコインのような 古くからある暗号通貨の様々なアイディアを借りて出来てきたとしても、 Ethereumには現在のプロトコルの機能を提供するための大半の一般的な方法とは異なる様々な点がある。 全くもって新しい経済的なアプローチをEthereumが開発しなければならなかった状況がある。 何故ならば、既存の他のシステムによって提供されて来なかった機能をEthereumは提供しているためだ。 この文書の目的は潜在的に自明ではないことを叙述し、もしくはEthereumのプロトコルを創りあげていく経過において、 議論の的となる物事に対して為されてきた決定についてを叙述し、同様に我々のアプローチと、その他の代替手段の可能性におけるリスクを示すことである。

原理

Ethereumのプロトコルの仕様は幾つかの原理に基づいている。:

  1. Sandwich Complexity Model: 我々はボトムレベルのEthereumの構造は出来る限りシンプルであるべきだと信じている。 そして、そのEthereuのインターフェイスは(開発者にとっての高位のプログラミングの言語、そしてユーザーにとってユーザーインターフェイスを含めて)出来る限り理解しやすいものであるべきだと信じている。 複雑であることが避けられないのであれば、複雑なことはプロトコルの"中間層"に押しやられるべきだ。 核となるコンセンサスの一部ではなく、エンドユーザーからも見ることが出来ない場所である。 例えば、高位の言語のコンパイラ、Serializationとdeserializationを行うスクリプトの引数、ストレージデータにとる構造のモデル、そのleveldbストレージのインターフェース、そしてwire protocol等だ、しかしながらこれらの設定は絶対的なものではない。

  2. 自由:ユーザーはEthereumのプロトコルの目的に制限されるべきではない。 そして我々はEthereumの特定のコントラクトや、特定の意図を持って作られたトランザクションに対して、賛成か反対かを選択的に述べようとするべきではない。このことは"本質的な中立状態"のコンセプトに基づく原則と似ている。 この原理に従わない例を1つ上げると、ビットコインのトランザクションプロトコルの中で ブロックチェーンを"オフレベル"の使い方(例えばデータストレージや、メタープロトコル)を阻害されることや、 ブロックチェーンを"認められていない”使われ方で使っているアプリケーションへの攻撃の目的のために、 あからさまな外面的なプロトコルの変更が(例えばOP_RETURNの規制が40バイトのこと)作られることが上げられる。 Ethereum では、我々はトランザクションのフィーをインセンティブと強く連動するように設計する思想に強く賛成している。 それはブロックチェーンの肥大を生み出す使い方をしてブロックチェーンを使うユーザーに対しては、彼ら自身にコストを内在化させるような使い方である。(例えば、ピグー税)

  3. 一般化: Ethereum におけるプロトコルとOpcodesの仕様は、出来るだけ低レベルのコンセプトを内包するべきである。そのことによって、現在は有用には見えないかもしれないが将来的には有用になりうる任意の使い方で構成されることが出来るようになる。 そしてそのために効率的に他の必要ではない機能を削ぎ落とすことに依って、ローレベルのコンセプトの設計は一層効率的になりうるだろう。 この原理に従う例は、DAPPSのフィードの情報(とりわけ軽いクライアント)を取るための、ログ用のLOG OPCODEの選択の例がある。 内部で早い頃から提案されたように、シンプルに全てのトランザクションとメッセージのログを取ることとは対照的な例だ。 "メッセージ"のコンセプトには、"関数の呼び出し"、"外部の観察者に対してのイベントリッスン"を含む、多くのコンセプトが集まっていて、メッセージと、トランザクションとは切り分けることが妥当であった。

  4. 我々は機能を持たない: 一般化に対しての必然的な帰結として、我々は直感的にプロトコルのパーツとなるような非常にありふれたハイレベルのユースケースの構築を断ることがある。 もし人々が本当にそれを欲しているならば、常にサブのプロトコル(例えばEtherをベースとしたサブの貨幣や、bitcoin/litecoin/dogecoin/sidechain等)として、契約の内部に作るだろうと考えているためだ。 例をあげるとBitcoinのロックタイムのような機能がEthereumの中には無い。 そのような機能は、"署名されたデータパケット"を送ったり、 そしてそのデータのパケットは特定の契約の意味で有用かもしれない;

  5. リスクを嫌悪しない: もしリスクが増大していくような変更がとても大きい利益をもたらすのであれば、我々は高いリスクを受け入れる。(例えば一般化された状態遷移や、50倍の速度のブロック承認時間、コンセンサスの合意性、等だ) これらの原則は全てEthreumの開発の方針であるが、絶対的でな方針ではない。 例をあげると、開発の時間を減らし、とても多くの急激な変更を一度に行わないようにしたいと思ってしまってことで ある変更を行うにあたって遅延をもたらしたことがある。その変更が明らかに将来のリリースに向けてメリットが有ることであったとしても。(例えば Ethereum1.1)

ブロックチェーンレベルでのプロトコル

このセクションでは、Ethereumで為されたいくつかのブロックチェーンレベルでのプロトコルの変更についての詳細を記載する。 どのようにしてトランザクションが機能しているのか、 そしてどのようにしてデータがシリアライズされ保存されているのか、アカウントの背後にあるメカニズムについてを記載する。

アカウントと、非UTXOs

ビットコイン、そしてビットコインに付帯する多くのデリバティブ、ユーザーのバランスについてのデータは、 使われていないトランザクションのアウトプットに基づく構造の中にある。 システム内のすべての状態は、"Unspent Outputs"(例:コイン)から成り立っている。 それは下記のような制約が有効であることのもとに、 それぞれのコインが所持者と価値を持ち、トランザクションは1つ以上のコインを使い、1つ以上の新しいコインを作り出す。

  1. あらゆる参照されているインプットは正しく、そして未だ使われていない状態である必要がある。
  2. 全てのインプットの場合において、トランザクションに記載されている署名はインプットの所持者のものと一致していなければならない。
  3. インプットの合計の価値は、アウトプットの合計の価値と等しいか、もしくは少なくなければならない。

システムにおける有るユーザーの残高は、上記のようにして、秘密鍵をもっているユーザーが有効な署名を作り出すことが出来るコインの合計の価値である。


(Image from https://bitcoin.org/en/developer-guide)

Ethereumは、より単純なアプローチに賛同し、ビットコインのスキームを投げ捨てる。状態はそれぞれが残高を所持しているアカウントの一覧を保持し、同様にEthereum特有のコードと内部のストレージデータを保持する。 そして、もし送り手が、送る分の価値に値する十分な残高を持っているならば、トランザクションは正当なものであり、 その場合において、送り手のアカウントから送った分の価値は引き落とされ、受け取り手は送られた価値を受け取る。 もし受取り手のアカウントがコードを持っていたならば、コードは実行され内部のストレージも同時に変更されるかもしれない。 もしくは受け取り手のアカウントのコードは新たな追加のメッセージを生成し、引き落としや、受け取りに繋がっている他のアカウントに送ることかもしれない。

UTXOsのメリットには下記がある。:

  1. 高水準ののプライバシー: もしあるユーザーが各々のトランザクションで新しいアカウントを使うのであれば、 アカウントとアドレスとを結びつけることは通常難しいだろう。 このことはとても通貨にとって適している性質だ。だが、任意のDapps(分散形のアプリ)としては適していない。 何故なら任意の分散形のアプリは複雑な塊となった情報のユーザーの状態を追いかける必要がよくあるため、 通貨システムのように簡単なユーザーの状態が切り分けられる状態が、存在しないかもしれないからだ。

  2. 潜在的にスケーラビリティのある仕組み: UTXOsは理論的にある種のスケーラビリティがあるパラダイムとよく適合している。 何故なら我々は、マークルツリー上で所有権を証明している幾つかのコインの所有者だけに依存しているため オーナーを含めた誰もがその所有権のデータを忘れると決めた場合でさえも、オーナーだけが損害を受けることで済むからだ。 アカウントの考え方では、すべての人が有るアカウントに対応するマークルツリーの一片を失うことで そのアカウントに対して送られているメッセージの実行が不可能になってしまう。 しかしながらUTXOに依存しないスキームにも、スケーラビリティが確かにある。

アカウントによるメリットは下記がある:

  1. 大きなスペースの節約: 例えばひとつのアカウントが5つのUTXOを持っているとした時、 UTXOモデルからアカウントモデルに切り替えれば、 必要なスペースは(20 + 32 + 8) * 5 = 300 bytes (20 はアドレスに, 32 はトランザクションIDに and 8は送信する価値に)から 20 + 8 + 2 = 30 bytes (20 はアドレスに, 8 は送信する価値に、2 はNonce(1度だけ使われる番号)に))となる。 実際のスペースの節約はこれほど大きなものにはならない、何故ならアカウントはパトリシアツリー上に保存される必要があるためだ。 しかしそうであったとしても大きな節約だ。 加えて、トランザクションはより小さくすることが出来る(例えば、Ethereum上の100バイトと、Bitcoin上の200-250バイト)、何故ならばあらゆるトランザクションは1回の参照と1回の署名を行い、1つのアウトプットを出すだけを必要とするからだ。

  2. 大きな代替性: ある種のコインの元となるブロックチェーンレベルでないコンセプトの存在に対して、 レッドリストやブラックリストを行うための仕組みを構築し、何に依拠しているコインなのかに判別を付けることは 技術的にも法的にも実用的な手段でなくなっている。

  3. シンプルさ: とりわけ一旦一層複雑なスクリプトが含まれたときに、より簡単にコードが書け、分かりやすくすること。 任意の分散形のアプリケーションをUTXOの考え方という、狭い場所へと押しこむことも出来るが、 根本的にスクリプトに対してどのようなUTXOに対して与えられたUTXOが使われる事が出来るかといったことを成約する能力を与え、 スクリプトが判別できる「アプリケーションの状態のルートの変更」のマークルツリーによる証明、を含めることを必要とすることは そんな概念は、ただアカウントという概念を使うことよりも一層複雑で汚いものだ。

  4. 定量で軽量なクライアントによる参照 : 軽量なクライアントはあらゆる点において、特定の方向に状態のツリーをみていくことで、 有るアカウントに関連する全てのデータにアクセスすることが可能である。 UTXOの考え方では、参照は各々のトランザクションごとに変更してしまうものであり、 長期的に稼働し、上記に述べたUTXO上の状態のルートを伝播するメカニズムを用いようとするDAppsに対しては、 とりわけ重荷となる問題である。

我々は任意の状態とコードを含んでいるDappsとやり取りしなければならないという問題のために、 アカウントを導入する事によるメリットは、代替手段を超える大きな方法であると決定している。 加えて我々の"機能を持たない"という原理の志のもと、人々が本当にプライバシーを気にするのであれば、 コインを混ぜるミキサーや、コインジョインの仕組みを プロトコルの署名されたデータパケットを通じて、コントラクトの内側に設計出来るだろうと考えている。

1つのアカウントの思想の弱点は、リプレイアタックを防ぐために、 全てのトランザクションは"nonce"一度だけ使う数値を持たなければならないことだ。 そのようなアカウントは最後に使われたnonce を追跡し、nonceが最後のnonceが使われた丁度一回後の場合にのみトランザクションを受け入れるようにする。これは、もはや使われなくなったアカウントでさえも、決してアカウントの状態から切り取られることが出来ないということを意味する。この問題の簡単な解決策は、トランザクションがブロックの番号を保持することを求めるようにすることだ。 一定の時間の後にしかリプレイが出来ないようにして、一定期間ごとにnonceをリセットする。 マイナーや、他のユーザーは使われていないアカウントを状態から取り除くために、使われていないアカウントへの"ping"を必要とするだろう。ブロックチェーンプロトコル全体の一部として、完全に消し去ることはあまりにもコストの掛かって不可能なオペレーションであるためだ。Ethereum のバージョン1.0では開発の加速のためにこのメカニズムを採用しなかったが、1.1ではこのようなシステムを使うだろう。

マークルパトリシアツリー

マークルパトリシアツリーは、アランレイナーによって構想され、Rippleのプロトコルの中に実装されたもので、 Ethereumの基礎となるデータの構造であり、全てのアカウントの状態の保存に使われている。 同様にトランザクションや、それぞれのブロック内のお釣りの保存にも使われている。 MPTはマークルツリーパトリシアツリー, の組み合わせであり、両方の要素を取って、下記の要素に従う形で構造を作り出している。

  1. 全てのユニークなKeyValueの組みは、独自のルートハッシュにマッピングされる。 そしてツリー上のKeyVelueの組みのメンバーシップを偽装することは不可能である。 (攻撃者が2^128以上のコンピューターパワーを持たない限り)
  2. 変更すること、追加すること、削除することは対数的な時間の中で可能。 これによって私達に効率的で更新が簡単な全体の状態を記載した我々のツリー上の"フィンガープリント"の方法を提供してくれる。 EthererumのMPT(Merkle Patricia Tree)は正式に下記に示されている。 https://github.com/ethereum/wiki/wiki/Patricia-Tree

MPTの上の仕様は下記の決定事項を含む:

  1. 2つのクラスのノードを持つ、KV(キーバリュー)のノードとそこから派生したノード(更なる詳細についてのMPTの仕様を見て欲しい)KVノードがあることでより効率的になる、何故ならばツリーが特定のエリアにまばらに存在することで、KVのノードがショートカットとしての役割を持つため、64層の深さのツリーを保つ必要が無くなるからだ。

  2. バイナリではないヘックスの派生ノード: これはルックアップの効率を改善するために為された。 我々は現在準最適な選択を行ってきた、何故ならヘックスツリーのルックアップの効率は まとまったノードを保存することによるバイナリーでの構想においてもシミュレーション出来からだ。 しかしながら、ツリーの構造を誤った形で構成することはとても簡単であるために、 最低でもルートの状態が合わなくなってしまうことには殆どならない。 我々は1.1のバージョン迄にそのような再構成を審議することを決定した。

  3. 空のバリューと、メンバーシップが無いことの区別を行わない事: シンプルさのために為された。組みになっていないバリューは一般的にゼロかか空のがゼロを表すために使われているという Ethereumのデフォルトの仕様とよく合っているからだ。 しかしながら他の一般性と、このように僅かばかり準最適な方法を取っていると強く思っている。

  4. 終了したノードと終了していないノードの区別: 技術的には、"ノードは終わりかけている"のフラグは必要が無い。 何故ならEthereumの全てのツリーは固定長のキーを保存するために使われているからだ。 だが、EthereumのMTPの実装が他の暗号プロトコルにそのまま使われるだろうと思い、一般性を高めるために付け加えた。

  5. **Sha3(k)を使うことに依って、キーが安全なツリーの中に保存されること(状態とアカウントを保存するツリーの中に使われている) ** このことは、64層の深さのノードから派生した最大限望ましくないチェインを設定して、 何度もSLOADとSSTOREを呼び出すことによって、DDoS攻撃をツリーに対して行うことを一層難しくしている。 より一層ツリーを列挙することを難しくために、もし列挙出来る限界をクライアントに設けたいと思うのであれば、 そのための最もシンプルな方法はデータベースマッピングをsha3(k)->k.RLPに対して行うことだ。

RLP

RLP(Recursive Length Prefix: 再帰的な長さの接頭辞)エンコーディングは、 Ethereumで使われている主要なシリアライゼーションの形式だ。 そして、Block,Transaction,アカウントの状態のデータ、そして配線プロトコルのメッセージといっった全ての場所で使われている。 RLPは正式に下記にて述べられている。https://github.com/ethereum/wiki/wiki/RLP

RLPは高度に最小主義なシリアライゼーションのフォーマットとして作られた。 ネストされたバイトアレイを保存する目的のためのみにある。 Protobufや、BSONや既存の他の解決策と違って、RLPはbooleanやfloatやdoubleや、integerさえも定義しようとしない。 代わりに只シンプルに、ネストされたアレイの形で構造を保存するために存在し、 アレイの意味を決定することはプロトコルに任せて、保存状態を残している。 Key Valueのマップは明示的にサポートされてはいないが、準公式なKeyValueのマッピングのサポートのための提案は、 k1, k2がある場所において、 [[k1, v1], [k2, v2], ...] をストリングに対する通常の順番を使ってソートすることだ。

RLPの代替手段として、ProtobufやBSONのような現在のアルゴリズムに使われていたかもしれないが、 しかしながら(1)実装のシンプルさ、(2)バイトが完全に一貫していることを保証するために我々はRLPを使うことが良いと考えている。 多くの言語におけるキーバリューのマップは明らかなオーダーを持たない、そしてフロートのポイントのフォーマットについて多くの特別な場合を持ち、潜在的に同じデータが違うコーディングとなることに繋がっている。そしてそのようにして違うハッシュ値となる。 プロトコルを内製することによって、我々はこれらのゴールを念頭に置いた仕様を保証することが出来る。(これは例えばVMのようなコードの一部にも当てはまる一般的な原理である。) BitTorrent にて使われているbencodeは、RLPに代わって通用する代替手段を持つかもしれない、 だがバイナリーのRLPに比べると長さを表すための小数のエンコーディングはやや準最適である。

圧縮のアルゴリズム

ワイヤプロトコルと、データベースがどちらもカスタムの圧縮アルゴリズムを、データ保存のために用いている。 そのアルゴリズムは、ゼロをランレングス符号化し、他の値はそのまま残すものとして、最も分かりやすく表現出来る。

compress('horse') 'horse' compress('donkey dragon 1231231243') 'donkey dragon 1231231243' compress('\xf8\xaf\xf8\xab\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe{b\xd5\xcd\x8d\x87\x97') '\xf8\xaf\xf8\xab\xa0\xfe\x9e\xbe{b\xd5\xcd\x8d\x87\x97' compress("\xc5\xd2F\x01\x86\xf7#<\x92~}\xb2\xdc\xc7\x03\xc0\xe5\x00\xb6S\xca\x82';{\xfa\xd8\x04]\x85\xa4p") '\xfe\x01'

この圧縮のアルゴリズムが存在する前には、多くのEthereum protocol 上の部分では数多くの特別な場合があった。 例えばsha3が良くsha3('') = ''としてオーバーライドされ, アカウント内にコードやストレージを保存する必要が無かったことから64バイトを保存しただろう。 しかしながらスペシャルキャラクターを最近取り除いたことに寄って、 Ethereumのデータ構造はデフォルトで一層束となった形になった。 ユーザーのデータベースにワイヤプロトコルを断続無しに導入することによって、 ブロックチェーンの外のレイヤーに機能的にデータを保存する代わりに、 データを機能的にブロックチェーンのプロトコルの外側に加える代わりにwire protocolに加える事に依って、 断続無くEthereumのデータをユーザーのデータベースに挿入することが出来る実装 このことはコンセンサスのレイヤーにおいて、モジュラリティを追加し、単純化する。 そして比較的簡単にデプロイを行うための圧縮アルゴリズムに対しての継続的な更新を行うことを可能にしている。 (例えばネットワークのプロトコルのバージョン更新)

ツリーの使い方

警告:このセクションではどのようにしてブルームフィルターが働くかの知識を前提としている。 導入として下記を参照 http://en.wikipedia.org/wiki/Bloom_filter

全てのEthereumのブロックチェーン上のブロックのヘッダーは、3つの樹に対してのポインターを含んでいる。 ブロックにアクセスした後の全体のツリー状態を示す、状態のツリー インデックスに依ってキーとされたブロック上の全てのトランザクションを示している、トランザクションのツリー (例えば key0: 実行される最初のトランザクション、key1 :2番目のトランザクション、等 ) そしてあらゆるトランザクションの"お釣り"に対応している、お釣りのツリーだ。 トランザクションのお釣りはRLPエンコードされた下記のデータの構造となっている。 構造: [medstate, gas_used, logbloom, logs]

詳細:

  • medstateは、トランザクションが執行された後のステートツリーのルートである。
  • gas_usedは、トランザクションのプロセスのために使われたgas の量である。
  • logsは、[address, [topic1, topic2... ],data]の形式のデーターのリストを表しており、 トランザクションの執行の間のLOG0 ... LOG4のopcodeによって作られている。(主要コールとサブのコールを含めて) addressは、そのログを生成したコントラクト上のアドレスを示している。topicは4 32バイトの値にまでなり、データは任意のサイズのバイトのアレイだ。 *logbloom は、トランザクション内の全てのログが基づくアドレスとトピックから構成されるbloom filterである。

bloom はブロックのヘッダー内、それはブロック内のトランザクションに対するの全てのbloomのORであるブロックヘッダーの中にある。 この構造の目的は、ライトクライアントにとってEthereumのプロトコルを様々な方法で出来るだけ使いやすい形にするためである。

Ethereumにおいてライトクライアントは、デフォルトでブロックのヘッダーをダウンロードし、 DHTをローカルのハードドライブ上のツリーのノードのデータベースとして使うことに依って、わずかな断片だけを承認していくと考えている。 幾つかの下記の使い方を含む:

  • ライトクライアントはアカウントの状態を特定の時間において知ることを望んでいる (nonce, balance, code, 又はstorage のインデックス)
  • ライトクライアントは単純に再帰的に状態のルートからツリーのノードをダウンロード出来る、任意の値に達する迄。
  • ライトクライアントは、トランザクションが承認されたかを確認したい。
  • ライトクライアントは単純にトランザクションのインデックスとブロックの番号についてネットワークに問い合わせることが出来る。 そして再帰的にトランザクションのツリーをダウンロードして利用可能かどうかを確かめることが出来る。 ライトクライアントは合計でデータの有効性を確かめたい。 それぞれのライトクライアントが、c[i]は1つのトランザクションのindex iをトランザクション T[i](R[i]のお釣りに対応する。) そして下記のように作動する。
  • R[i-1].medstate、 R[i-1].gas_used(if i = 0 use the parent endstate and 0 gas_used)を生成
  • T[i]のトランザクションの実行
  • 結果として生じるState Root が R[i].medstateであり、その gas_used がR[i].gas_usedであることを確認 ログと、出来上がったbloomの組みが R[i].logsとR[i].logbloomと合うことを確認
  • bloomがブロックヘッダーレベルのbloomのサブ集合(これはブロックヘッダーレベルのBloomが間違っている事を検出する)であることを確認。そして幾つかのランダムのブロックヘッダーレベルのインデックスを取得し、 ブルームが1つか他のトランザクションレベルのブルームを持ち、 もしレスポンスが与えられていなかった場合には、ブロックを拒絶する。
  • ライトクライアントは記録されるイベントを"watch"したいと思う。 その場合のプロトコルは下記に従う。
  • ライトクライアントは全てのブロックのヘッダーを取得し、ライトクライアントが求めるアドレスかトピックのリストの1つと適合するbloom filterを、ブロックヘッダーが含んでいるかを確認する。
  • 潜在的に適合しているブロックヘッダーを見つけた場合、ライトクライアントは ブルームフィルターに適合する全てのトランザクションのレシートと、トランザクションに対する証明を取得する。
  • 潜在的に適合しているトランザクションを見つけた場合、ライトクライアントは実際のLOGのRLPを確かめ、それが実際に適合するかどうかを確認する。

最初の3つのライトクライアントのプロトコルでは対数量のデータ・アクセスとコンピュテーションが必要となる。 最初の4つ目では~0(Nの平方根)ブルームフィルターから只2階層だけ、だがこのことは0(log(N))に対して改善が為される。 もしライトクライアントが様々なプロバイダーに依拠しようとするのであれば、"興味を持っている”トランザクションのインデックスを指し示し、もし間違ったトランザクションを所持していることが公開されれば、プロバイダーへの依存を解除うsる。 その最初のプロトコルは単純に状態を確認するために有用であr,2爪のプロトコルは、消費者と承認のようなシナリオにおいて、トランザクションが認証されたどうかを確認するために有用である。 3つ目のプロトコルは、Ethereuのライトクライアントがブロックをとても少ない量のトラストによって承認出来るようにしている。 ビットコインでは、例えばマイナーはマイナーに対して多大な料のトランザクションの費用を払うブロックを生成することが出来る。 そして軽量ノードにとってこのことを知る術はない、また、正直な全てのノードが検知することが出来た場合にも その不当性を証明しなければならない Ethereumでは、もしブロックが不当な場合には、不当な状態の遷移をどこかのindexで含んでいなければならない、そして ライトクライアントは、何か間違っていることは無いかindex が見つけられるように認証することとなる。もしくは、データが使用不可能なためにその証明のステップが終わらないからだ、そしてクライアントは警告を上げることが出来る。

4つ目のプロトコルはDAPPが効率的に証明される必要のあるある種のイベントを追跡したい場合に有用である。 例えば分散形の交換所でトレードのログを取るか、もしくはウォレットがトランザクションのログを取る(ライトクライアントが高いレベルのコインベースとアンクルのチェックがマイナーのアカウントに対して十分に働いた場合) Bitcoinの言葉でいうと、ログは純粋な"Proof of publication"のopcode として考えられる。

Uncleへのインセンティブ

The 強欲で最も重い監視されているサブツリー"(GHOST)プロトコルは、最初にYonatan Sompolinsky とAvivZoharによって2013年12月に導入されたイノベーションだ。first introduced そしてもっと早いブロックの時間を防ぐための問題解決策としての最初の重大な試みでもある。 GHOSTが開発された動機は、承認時間の早いブロックチェーンが、古くなっていまう可能性が高いためにが 現在セキュリティリスクを抱えていることだ。 何故ならブロックはネットワークに伝播するまでに一定時間必要であり、 もしマイナーAがを採掘してマイナーBが他のブロックをマイナーAのブロックが伝播するまでにほった場合には、 マイナーBのブロックは結局無駄("stale")してしまい、その仕事はネットワークのセキュリティの改善に寄与しないためだ。 更に言えば、そこには中央集権の問題がある。:もしマイナ-Aがマイニングプール上で30%のハッシュパワーを持ち、Bは10%のハッシュパワーを持っているならば、Aは70%の時間無駄になるブロックを生み出すリスクをもっており、(Aが最後のブロックを生成した他の30%の時間から、そしてマイニングのデータをすぐに手に入れることになるだろう。)一方でBは90%の時間において無駄になるブロックを生成するリスクを取っている。 このようにして、ブロックのインターバルがブロックが無駄になる確立が十分に高くなるほどに短かったとしても Aはそのサイズのお陰で大変効率的でシンプルに採掘を行うことが出来る。

これらの2つ効果を組み合わせると、ブロックチェーンはブロックを早く生成し、 1 早くブロックを作り出していくブロックチェーンが存在することで、 1つのマイニングプールが、マイニングのプロセスに対してネットワーク全体のハッシュパワーの十分に大きな割合を持つことで デファクトのコントロールを持つことに繋がって行きやすくなるだろう。

Sompolinsky とZoharによって述べられたように、GHOSTはネットワークのセキュリティロスの最初の問題を、 どのブロックが"最長"のブロックかを計算する中で、無駄となってしまったブロックを含める事によって改善した。 即ち、ブロックの親と先祖を含めるだけでなく、無駄になった先祖のブロック(Ethereumの隠語で、"uncles")も含めることで、 どのブロックが合計で最も長いproof of workのための計算に加えられることだ。

2つ目の中央集権による偏りの問題を解決するために我々は異なる戦略を採用する: 我々は無駄になったブロックに対しての報酬を無駄となったブロックに付与すること: 無駄となったブロックが7/8(87.5 %)のベースのリワードを受け取る事が出来、 そして無駄となったブロックを含む甥が、ベースのリワードの1/32(3.125 %)をブロックを含めた報酬として受け取る事が出来る。 しかしながら、トランザクションの費用は甥にもおじにも与えられることはない。

Ethereumでは、無駄となったブロックはおじによってのみ含まれる事ができ、7代目迄のその直接の兄弟の子孫が含まれ、 そして一層の関係のブロックは含まれない。これはいくつかの理由によって為されている。 初めに、無限のGHOSTはどのおじが与えられたブロックが有効かという計算を行うに当たって大変複雑となるからだ。 2つ目には、無限のおじに対してのインセンティブがEthereuで使われることで、 チェーンへの公衆の攻撃者ではなく、 メインチェーンのマイナーにとって採掘するインセンティブを失わせることに繋がるからだ。 最後に、多くのネガティブな影響無しに、7段階目迄に制限していることが大半の望ましい影響を及ぼしていることについての計算を示す。

ブロックの承認時間のアルゴリズムについての仕様は下記を含む

  • 12秒のブロック承認時間: 出来るだけ早い時間として12秒が選ばれた。 だが同時に、ネットワークの遅れよりもかなり長い時間である。 2013年のDecker とWattenhoferによる論文では、Bitcoinのネットワークの遅れが計算されており、 それは12.6秒が新しいブロックが残りの95%のノードに伝播する迄に必要なためだ。 しかしながらその文書で指摘されていた伝播に掛かる全体の時間はブロックのサイズに比例するとも指摘されている。 そのようにして、速い通貨において、伝播のための時間は、劇的に減少することが期待される。 伝播に掛かる待機時間は凡そ2秒だが、安全のために我々の分析では12秒が伝播のために掛かると考えている。
  • 7ブロックの子孫の制限: これは幾つかのブロックの後、ブロックの履歴をすぐに"忘れやすい"ものへとするための仕様だ、 そして7ブロックが最も良い影響を与えることが証明された。
  • 1ブロックの子孫のリミット (例: c(c(p(p(p(head))))), where c = chile and p = parent, is invalid): このパートではシンプルさを仕様目標とし、上記のシミュレーターでは大きな中央集権型のリスクを持たないことを示す。
  • Uncleブロックの正当性の要求: Uncle(おじ)は認証されたブロックではなく、認証されたブロックヘッダーでなければならない。 これはシンプルさを達成するためであり、ブロックチェーンのモデルを維持するために線形のデータ構造を取る(block-DAGではなく、SompolinskyとZoharの新しいモデル)。おじが正当なブロックであることを求めるのは同様に正当なアプローチである。
  • 報酬の分散: 7/8のベースマイニングの報酬はおじにいき、残りの1/32は甥にいき、0%のトランザクション費用が双方に渡る。 もし費用が独占されているならば、中央集権的な考え方からみるとおじに対してのインセンティブが有効でなくなるだろう。 しかしながらこれはEthereumで、出来るだけProof of Work を使い、どうしてEtherを発行し続けようとしているのかという一つの理由である。

困難さをアップデートするアルゴリズム

Ethereumにおいての困難さの更新は下記のルールに従って行われる: diff(genesis) = 2^32 diff(block) - diff.block.parent + floor(diff.block.parent/1024)*

The difficulty in Ethereum is currently updated according to the following rule:

diff(genesis) = 2^32 1 if block.timestamp - block.parent.timestamp < 9 else -1 if block.timestamp - block.parent.timestamp >= 9

diff(block) = diff.block.parent + floor(diff.block.parent / 1024) * 1 if block.timestamp - block.parent.timestamp < 9 else -1 if block.timestamp - block.parent.timestamp >= 9 The design goals behind the difficulty update rule are:

このdifficulty のアップデートの仕様は下記である。

  • 速いアップデート: ブロック同士の間での読込が早ければ速いほど与えられたハッシュパワーの増大と現象にフィットする物となる。
  • 低いボラティリティ: Difficulty はハッシュパワーがもし定数であるならば大きく変動するべきではない。
  • シンプルさ: アルゴリズムは比較的シンプルに実装できるべきである。
  • メモリーを食わない: アルゴリズムは幾つかのブロックの履歴に依拠するべきでなない、 それは幾つかのメモリーの変数を出来る限り含むべきである。 最後の10ブロックを考えると、最後の10ブロックのブロックのヘッダーに全てのメモリーの変数が置かれる。 それはアルゴリズムが作用するために利用可能な全てのデータである。 セキュリティーホールをなくす: アルゴリズムはマイナーに対して 彼らのレベニューを最大化させようという意図で、 タイムスタンプや、マイニングプールでのハッシュパワーの増減の繰り返しをさせることを過度に奨励すべきではない。 我々の現在のアルゴリズムは、高い副次的適合性を低いボラティリティと、セキュリティホールの防止策に対して準最適な方法として 既に決定している。そしてタイムスタンプを親や、祖父母のブロックとタイムスタンプを比べるように変更し もしマイナーが2つの列になったブロックのマイニングを行っているならば、 マイナーがタイムスタンプの書き換えに対してのみインセンティブを持つようにしようとは全く考えていない。 もう一つのシミュレーションを伴う強力な定式はこちらにある。 https://github.com/ethereum/economic-modeling/blob/master/diffadjust/blkdiff.py (シミュレーターはビットコインのマイニングパワーを用いているが、日ごとの平均を全体に対して使っている。1つのポイントでのシミュレーションは95%1日の内にクラッシュする。)

GasとFee

全てのビットコイン内のトランザクションが殆ど同じであるとしても、ネットワークに対するコストは一つのユニットの中で形作られているために、Ethereumのトランザクションはより複雑である。そして、トランザクションのFeeのシステムは、ストレージのコストと、コンピュテーションのコストを含めて様々な要素を考慮に入れて設計されている。 とりわけ重要な事は Ethereumのプログラミング言語はチューリング完全であることだ。 そしてトランザクションはbandwidthを使っているかもしれず、ストレージは任意の量のコンピュテーションw行っているかもしれない。 そして後半では先行して確かにわかったかもしれないが 先行して知ることは決して出来ないコンピューターの停止問題があるが故に、 無限ループによるサービス妨害攻撃を防ぐために鍵となる目的である。

トランザクションの費用の背後にある基本となる作用は下記のとおりだ。

  • 全てのトランザクションは必ず"gas"の量を定義しなければならない。"gas"は(startgasと呼ばれる)使われようとし、 そのフィーはgasの大きさに応じて支払われる(gasprice)。そして、プログラム開始時に、startgas* gasprice 分のetherが送り手のアカウントのトランザクションから取り除かれる。
  • トランザクションが実行されている際に、全てのオペレーション、databaseの読み書き、メッセージ、全てのコンピューテーションは、 仮想マシーンによって 一定量のgas を消費することによって行われる。 もしトランザクションの実行プロセスが完全に、一定量の限界よりも少ない量のガスを消費するのであれば、gas_rem(gasの残り)とともに行われる。そしてトランザクションが通常どおり執行されて、トランザクションの最後には、トランザクションの送り手はga_rem* gasprice分の返金を受け取る。そしてブロックのマイナーは(startgas-gas_rem)*gasprice分の報酬を受け取る。
  • もしトランザクションが"ガス切れ"に実行の最中でなった場合には、全ての実行プロセスは元の状態に戻り、 トランザクション自体は正しいトランザクションであったにもかかわらず、トランザクションの影響は、 ただ合計のstartgas*gasprice がマイナーに行き渡るだけとなる。
  • あるコントラクトがメッセージを他のコントラクトに送るときには、それはメッセージから現れたsub実行に対してのgaslimitを設定するオプションを持っている。もしsubの実行プロセスがgas切れになった際には、subの実行プロセスは以前の状態に戻され、 そうであったにも拘らず、gasは消費される。

これらのコンポーネント必須要素である・例えば:

  • もしトランザクションがgasの上限を定めなくて良い場合には、悪意あるユーザーは何10億というループを作り出すトランザクションを送り、そのようなプロセスがブロックの同士の間隔よりも長くなってしまうことで、誰もプロセスを行うことが出来なくなる。 だが、マイナーが事前にわからないんのであれば、サービス妨害攻撃に対する脆弱性へと繋がる。

  • gas-countingを規制する他の選択肢は、時間制限である。 何故ならばあまりにも他の(有るマシーンが他のマシーンよりも早い場合には同一するマシーンのクローズコールが常に存在する。) Startgas * gaspriceの全体の価値は最初の時点でデポジットとして取り除かれるべきである、それによって 有るアカウントが自身の動作の実行によって、gasのコストが払えなくなって"破産する"こともなくなるような シチュエーションが起こることを防ぐ事が出来る。 もし実行がgas が足りないためにやり直す事ができない場合には、コントラクトはより強固で難しいセキュリティの手段を

  • 只十分なガスが途中までしか足りていないトランザクションやメッセージによって それによってコントラクトの実行における幾つかの変更に繋がるが他にはつながらない。

  • もしサブのリミットが存在しない場合には、敵対的なアカウントが、 他のアカウントに対して合意を導入することによって、最初のコンピュテーションにおいて無限ループを導入し、 被害者のコントラクトによるどんな攻撃社のコントラクトや、コントラクトに対してメッセージを送ることが全てのトランザクションの実行を枯れさせてしまうかもしれない。

  • トランザクションの送り手がコントラクトの代わりに、gas を送ることを要求することは、開発者の使い勝手をかなり大きく改善した。 まさにEthereumが始まったばかりのころ、gasに対して費用を払うコントラクトがあったが、 全てのコントラクトが"guard"するためのコード、即ち全ての受信するメッセージが、消費されるgasに対して支払うための十分なetherがあるかどうかををするためのコードを持たなければならないという、かなり汚い実装となってしまう問題に遭遇した。

下記がgasのコストについての仕様である。

  • 21000gas が全てのトランザクションに対して”base fee"としてチャージされる。 これは楕円曲線のオペレーションを、送り手の署名からアドレスを再現し、 同様に、トランザクションを保存しておくディスクと一定時間に処理できる情報量の空間を確保しておくための費用である。

  • トランザクションは無限の量のデータを含んでいる、そして仮想マシーンの中にはopcodeが存在している。 仮想マシーンはコントラクトがデータにアクセスするためのトランザクションを受け取ることを可能としている。 データに対してのgasの費用はゼロバイトに対して1gas、ノンゼロバイトに対して5 gasである。 ユーザーによって書かれたコントラクト中の大半のトランザクションにおいて、 32バイトの引数の列に構成されるとわかったため、この計算式とした。 大半のコントラクトはゼロバイトになり、そのようなコンストラクションは不十分のように見えるが、 実際には圧縮アルゴリズムのおかげによって、効率的なものである。 我々はより複雑な期待されるバイト数に基づいて引数を厳しくパック仕様とするメカニズムのかわりとなる使い方を奨励したいと思っていて、 まさにかなりの複雑さをコンパイラのレベルで増大させることへと繋がった。 これは、Sandwich Complexity modelの例外であるが、コストに対するベネフィットが大きいために行われるべきと考えられたことである。

  • SSTOREのopcodeのコストは、アカウントのストレージのバリュ0、どちらかである (i)20000gasがゼロバリューからノンゼロのバリューに変わる時に掛かる。 (ii)5000gas がゼロバリューからノンゼロのバリューに変わるときに掛かる。 (iii)5000gas がノンゼロのバリューからゼロバリューに代わる時に掛かる、加えて20000gasのりファンドが成功したトランザクションの最後尾に与えられる。(例えば 実行gas切れの例外にならないような実行) Refundsはトランザクションによって消費される全体のガスの50%の部分に制限され、 このことはストレージを消去するために少ないながらのインセンティブを提供する。 インセンティブの仕組みが欠けているために、多くのコントラクトがstorageを使わない状態で残すだろうと我々は気づいた。 我々がそのようなインセンティブのシステムが欠けていることに築き、ストレージが使われないままで残っていると、 早くブロックチェーンの肥大へと繋がるだろう、そして大半のベネフィットを"利子を徴収する"。 そして遅れたrefund のメカニズムは、アタッカーによって僅かな量のgasと共にトランザクションが送られ、何度も大きな量のストレージのスロットがクリアーされる類の攻撃に対して サービスの否定攻撃を防ぐために必要である、 長いループの一端となる、そしてgas切れとなることで、大量の承認者のコンピューターパワーを実際にはストレージをクリアすることもgas を消費することも無く行わせてしまう。 この50%のcapは、マイナーに対して定量のガスト共に与えられたトランザクションを、 マイナーが、トランザクションを実行するためのコンピューターパワーに対して課された上限を図るために必要である。

  • コントラクトによって与えられたメッセージの中のデータに掛かるgasのコストは無い。 これはメッセージコールの最中に、呼ばれたデータをコピーする必要は無いからだ、 何故なら呼ばれたデータは、子の実行が進行中であった最中でも変わることのない 親のコントラクトのメモリーへのポインタとしてシンプルに考えることが出来るためである。

  • メモリーは無限に拡張可能なアレイだ。しかしながら32バイトのメモリーごとのメモリーの拡大に対してgasが掛かる。(切り上げ)

  • 幾つかのopcodeではコンピュテーションの時間は大きく引数に依存する、様々なgasのコストを持ちうる。 例えば、gasのコストとなるEXPが10+10バイトの指数であるとして(例, x^0 = 1 gas, x^1 ....x^255=2gas, x^256 ... x^65535 = 3gas, etc) Copuするためのopcodesに掛かる(CALLDATACOPY, CODECOPY, EXTCODECOPY)gasのコストは 32バイトのコピー毎に対して、1 + 1掛かる。(切り上げ)(LOGも同様のに似ているルール). メモリーの拡大に対してのgas残すとは、カバーするために十分ではない、quadraticな攻撃に対して開いている状態であるからである。 (50000 回の 50000gasを消費するCALLDATACOPYを行い 50000^2のコンピューティングの費用だが、50000以内のgasしかgasのコストの変数が導入される前に提供されていないからだ。)

  • CALLのopcode(そしてCALLCODEも同様に)そのバリューがノンゼロの場合には追加の9000gasを消費する。 その理由はあらゆる価値の以降は、書庫となっているノードのための履歴のストレージに対して大変大きな肥大を招くためだ。 実際のフィーは6700しかかかっていないが、我々は2300の人工的なgasを追加している、これは自動的に受信者に与えられるものだ。 これはトランザクションを受け取るウォレットがトランザクションのログを作るために十分がgasを持っていることを保証するためだ。

gasのメカニズムについての他に重要な点は、gasの値段自体が経済となっていることだ。 デフォルトの方法では、bitcoinで使われているように、純粋にボランティアの費用を落ち、 マイナーがゲートキーパーとして働くことに依拠しており、ダイナミックな最低限の費用を設定することだ。 Ethereumにおける同様の手段は、トランザクションの送り手が、自由にgasのコストを設定することを許すことだ。 この方法では、Bitcoinのコミュニティに対して大きく賛同された、とりわけそれが"市場に基づいていて”、 供給と需要が存在し、マイナー同士とトランザクションの送り手同士で値段を決定することを可能としているからだ。

この原理の問題は、しかしながらトランザクションのプロセスは市場には無いことだ、 マイナーがセンダーに提供するサービスとしてトランザクションのプロセスに対して構文解析を行うことは直感的に魅力的だが、 実際には全てのマイナーが含むトランザクションはネットワーク全体によって行われる必要がある。それ故にトランザクションに掛かるコストの殆ど大半は第三者に対して生じるものであり、トランザクションがブロックチェーンに含まれるかどうかを決定するマイナーに対してではない。それ故に、こうした一般的な問題はとても良く起こるのだ。

現在、現実にはどのようにしてマイナーが作業しているのかについての情報が欠如しているために、私達は大変シンプルなアプローチを行おうとしている。それは投票システムである。 マイナーは最後のブロックの1/1024のgas limitとなるように、 gas のlimitを現在のブロックに対して設定する権利を持っており、 そして結果もたらされるgasのリミットは、マイナー達の設定の中央値となるべきである。 将来我々がソフトフォークを可能にしてより正確なアルゴリズムとするだろう。

仮想マシーン

Ethereumの仮想マシーンは、トランザクションのコードを実行させるエンジンである。 それはEthereumと他のシステムの根本的な違いだ。 仮想マシーンがコントラクトとメッセージのモデルを分割したものとして考えるべきであることに注意して欲しい。 例えば、SIGNEXTENDのopcodeはVMの機能の機能だが、実 際にはコントラクトは他のコントラクトを呼び出し、サブコールに対してのgasの限界を定める事ができ、 コントラクトとメッセージのモデルの一部におけるsub-callを行う事が出来る。

EVM内の設計原理は下記である。

  • シンプルさ: 少なく、そしてローレベルのopcodeで出来るだけ行う。なるべく少ないデータ型でなるべく少ない仮想マシーンレベルの設計を行う。
  • 統合的な決定性: 絶対的なVMの仕様のどの部分においても曖昧な部分は許されない、そしてその結果として 完全に決定的なものとなる。加えて、gasの消費量を計算するために正確なコンピュテーションの段階のコンセプトがあるべきである。
  • 容量の節約: EVMのアセンブルは出来るだけコンパクトでなければならない。(例; 4000バイトの基本サイズを持つCプログラムは受け入れられない)
  • 期待されるアプリケーションに対しての特化性: 20バイトのアドレスと、32バイトの値を持つカスタムの暗号を取扱うための能力、 カスタムの暗号化を行うための数学的なモジュール、ブロックとトランザクションのデータを読み込むこと、状態とやりとりする能力
  • シンプルなセキュリティ: VMをセキュリティホールが無いものにするためのgasコストを利用するモデルを思いつくことは簡単だろう。
  • 最適化に対して使い勝手が良いこと:最適化を導入するために簡単であること。JITでコンパイルされたものや、その他の方法で速度が改善されたバージョンのVMが構築出来るようにするためだ。

これ迄幾つかの特定の使用に対する決定がなされてきた。

  • 一時的/永続的なストレージの区別 仮想マシーンのインスタンスの中に存在し、VMの実行が終わると同時に消滅する一時的なストレージと、 アカウント毎にブロックチェーンに刻まれる状態のレベルで存在する永続的なストレージの2つには明確な区別が存在する。 例えば、下記のようなツリー型の実行が行われることを考えてみよう。 (Sは永続的なストレージに対して、そしてMは一時的なストレージに対してである。): (i) AはBを呼び出す。 (ii)Bは B.S[0] = 5, B.M[0] = 9 (iii) B はCを呼び出す (iv)CはBを呼び出す、ここでBがB.S[0]を呼びだそうとすれば、Bには以前から保存されていた値を受け取る。 (v)だがmBがBを呼びだそうとし、この点において、もしBがB.M[0]を呼び出せば0を受け取る、なぜならば仮想マシーンの新しいインスタンスは新しい一時的なストレージを持つからだ。 もし、BがB.M[0] = 13 とB.S[0] = 17 を内部のコールの中で行うと、この内部のコールもCのコールも終了し、 Bの外部コールへと実行が戻される。 この区別の目的は、 (1)互いの実行うインスタンスが再帰的な呼び出しにより破壊されることのない自身のメモリーを与えるため。セキュアとし、プログラミングを簡単にするため。 (2)とても早くメモリーを操作できる形式を提供すること、何故ならストレージの更新はツリーを変更する必要があるため、遅い必要があるからだ。

  • スタック/メモリーモデル -この決定が早くから為されたのは3つのタイプのコンピューターの状態を持つためだ。 (プログラムのカウンターが次のインストラクションを指していることを脇においてだ。) その3つの状態はスタック(通常の32バイト値のLIFOスタック)、 そしてメモリー(無限に拡張可能な一時的なバイトアレイ)と ストレージ(永続的なストレージ)だ。 一時的なストレージ上では、スタックとメモリーの構成の代替は、メモリーだけの構成、もしくは いくつかのレジスタとメモリーのハイブリッドの構成。(レジスタとメモリはあまり違わない、通常はレジスタは一種のメモリのため) そのような状況において、あらゆるインストラクションは3つの引数を取っている、例えばADD R1 R2 R3: M[R1] = M[R2] + M[R3]. スタックの考え方に従えば明らかな理由のために選ばれ、4倍以上コードを小さくする事が出来る。

  • 32バイトの文字のサイズ 4か8バイトの単語の代替として、大半の他の構造で使われているように、もしくは無数にビットコインで使われているように。 4 or 8 バイトの単語ではあまりにも制限が厳しくアドレスや、暗号の計算のための値を保存することが出来なかったり、 制限が無い場合にはが安全なgasのモデルを作るためにあまりに厳しい場合がある。 只十分に多くの暗号の実装で共通している32バイトの値は、十分に大きいからである、、それはアドレスについてもあてはまる(アドレスをパックして、単独のストレージの値へと挿入する最適化の方法として)、だが究極的に非効率というほど大きくはない。

  • 我々独自のVMを持つこと:代替案としてJavaを使うことや、幾つかのLispや、Luaのプログラミング言語を使うことが考えられるが、 我々は特別なVMを持つことが適していると決定した。 何故ならば (i)我々のVMの仕様は他の多くのVMよりも大変シンプルだからである。 何故なら他の仮想マシーンでは複雑さのために大変安いコストを払わなければならない、 一方で、我々の場合、複雑なユニットを作り出すことは、開発が中央に集中している状況を作り出す高いバリアへと繋がり、 コンセンサスが出来なくなるような、セキュリティホールの潜在的な可能性となるからだ。 (ii)より一層VMを特殊に作る上げる事が出来る。例えば32バイトのサイズの宣言を持つことだ。 (iii)インストール自体が難しくなってしまうというような、複雑な外部依存を持たないでいられる (iv)我々の特定のセキュリティのニーズにEthereumに対しての全てのセキュリティのレビューを行うために外部のVMのセキュリティのレビューを行うことを余儀なくされてしまうだろう、そしてその努力で得られるものはあまり大きくない。

  • 様々な拡張可能なメモリーのサイズを使うこと。 もしサイズが小さく、もしサイズが大きければ不必要にコストが高くなるのであれば、我々は固定長のメモリーの長さの制約が必要無いと考えた。 そしてもしメモリーアクセスのための宣言が必要無いのであれば、外部へのアクセスをチェックするどのような場合においても、固定長の長さは実行をより効率的にするだろう。

  • スタックサイズの制限を持たないこと 特定の理由が無い。多くの場合gasのコストと、ブロックレベルのgasの制限を組み合わせることに依って、制限は必ずしも必要ないことを心に留めて欲しい、 これら2つの組み合わせによって、全てのリソースの消費に対して常に天井を設ける働きを実行できる。

  • コールの深さを1024に制限すること 多くのプログラミング言語では、スタックが深い点において、 多くのメモリーの使用かコンピューター負荷によってプログラムが壊れるよりも一層早く、 そのためにブロック内ののgasの制限による暗黙的なリミットだけでは十分ではない。

  • **型を持たないこと シンプルさのために行った。代わりに、符号付きと符号なしのDIV, SDIV,MOD,SMODを示すopcodeを使った、(それはADDやMULのためにあり、ADDやMULの符号付きと符号なしのopcoceは同様のものだ。) そして固定長の場所に対しての算数(深い、固定された場所、数学的に他のベネフィットのある32バイトの文字)は皆シンプルである。 例えば32ビットの深さでは、a * b -> (a * b )/ 2^32 , a / b -> a * 2^32 / b and +, - and * are unchanged from integer 仮想マシーンにおいて、関数と幾つかのopcodeの目的は明らかである、しかしながら他のopcodeはあまり自明ではない。 例えば特定の理由は下記のように上げられる。

  • ADDMOD, MULMOD: 大半の場合において、addmod(a, b, c) = a * b % cであるがしかしながら、 楕円曲線暗号の多くのクラスは特定の場合ではそしてそのためにa * b % cを直接行うことで、それ故に実際に行っている。 そして完全に異なる結果を生み出すこととなる。 ある形式において、a * b % c を32バイトの値と共に32バイトのスペースにて行うことはかなり重く厚い処理となってしまう。

  • SIGNEXTEND: SIGNEXTENDの目的はtypecastingを大きな符号付き整数から、小さな符号付き整数へと型変換するためだ。 小さな符号付き整数は有用である、何故ならばJITに依ってコンパイルされた仮想マシーンの環境では、 32バイトの整数を第一義に扱って長く走っているコードの塊を検出し、かなりの速度向上が見込むことが出来るかもしれないからだ。

  • SHA3: Ethereumでは、SHA3はとても応用性が高く、セキュアで無限の大きさのハッシュのマップが出来る。 ストレージを扱うハッシュのマップは悪意ある衝突を防ぐために、 セキュアなハッシュのファンクションを使う必要が出てくるだろう、同様にマークルツリーを認証するためにもそしてEthereumのようなデータの構造を認証するためにも使われるだろう。 鍵となる点は、SHA256や、ECRECOVERやRIPEMD160はopcodeではなく、仮のコントラクトして含まれているが、SHA3はOPCODEとして含まれている点だ

その目的は分割されたカテゴリーに暗号化の手段を分けることによって、 もし、いつか正しい”ネイティブの拡張"によるシステムを考案した時に より多くのそんなコントラクトを、opcodeの空間を埋めること無く加える事が出来るようにするためだ。

  • ORIGIN: トランザクションの送り手を与えるORIGINのopcodeを使う第一の目的は、コントラクトがgasのためにリファンドの支払いが出来るようにするためだ。

  • COINBASE: COINBASEの第一義的な使い方は、 (i) 補助貨幣がネットワークのセキュリティの向上に役立つようにするため。 (ii) Schelling coinのような副次的な分散形のアプリケーションのための、分散形の経済のセットとして マイナーが使えるようにするようにするためだ。

  • PREVHASH: 程々にセキュアなランダムさを生み出すソースとして利用される。 コントラクトが、マークルツリーの以前のブロック内のproof of stateを、 とても複雑で再帰的な"Ethereum内、Ethereumライトクライアント"の構造を必要とすること無く 確かめる事が出来るようにするためにある。

  • EXTCODESIZE , EXTCODECOPY: 第一義的な使い方はコントラクトが、他のコントラクトとやり取りを開始する前に、 他のコントラクトのコードがテンプレートの定義に反していないかをチェックするか、 模倣していないかさえもチェックする事が出来るようにするためにある。 下記に使い方がある。http://lesswrong.com/lw/aq9/decision_theories_a_less_wrong_primer/  (アプリケーション用)

  • JUMPDEST: jumpの行き先が制限されており、幾つかのインデックスに制限されていたときに、 JITでコンパイルされた仮想マシーンがより一層簡単に実装でき、 様々な行き先でのコンピュテーションの複雑さがおおまかにO(log(正しいjumpの行き先の数))、だが 固定的なjumpは常に定数時間を要する。) それ故に、我々は (i)正当に認証された変数のjumpの行き先に対しての制約 (ii)動的なjumpに対して静的なjumpを使うことのインセンティブ 2つのゴールを満たすためには、我々は(i)直ぐにプッシュによって先行されるjumpは他のjumpにのみjumpすることが出来る。 (ii)他のjumpはJUMPDESTにのみjumpを行う事が出来る。 jumpに対してのjumpを制限することは、jumpは動的なのか静的なのかという質問がただコード内の以前のオペレーションを見るだけで判別することが出来るようになる。 プッシュデータに対してのjumpを禁じることはJITの仮想マシーンのコンパイルと実行を早める事にも繋がる。 LOG: LOGはイベントをログするためにある、上記のtrieの使い方のセクションを参照

  • CALLCODE: 目的はあるコントラクトがスタックとメモリーが分割されている他のコントラクトに保存されているコード形式となっているfunctionsを呼び出すことが出来るようにし、そのコントラクトのストレージ内で使うことを可能にするためだ。 これはスケール出来るような形でライブラリー型のコードをブロックチェーン上に実装することをかなり容易にしている。

  • SUICIDE: もしもう必要無いのであればmこのopcodeはコントラクトがすぐに自分自身を消去する。 最後に実行されるトランザクションにてSUICIDEは実行されるが、すぐに実行されるわけではない、 なぜならば、既に実行されたsuicide(自殺)を差し戻すことが出来るようにするということは キャッシュの複雑さを著しく増大させ、仮想マシーンの効率的な実装において大変な困難が強いられるという事実があるためだ。

  • PC: 全てのPCのopcodeのインスタンスは、そのインデックスにある実際のプログラムカウンターにpushとして挿入されているため、 理論的には必ずしも必要ではないが、コード中でPCを使うことに依って、位置に依存しないコードを作る事が出来る。 (例えば、コピー/ペーストされて他のコントラクトに入ってしまい、コンパイルされた関数が、 異なるインデックスになってしまっても、プログラムを破壊しない)

Clone this wiki locally