アルゴリズムとかオーダーとか

仕事で勉強したことなどをまとめてます

EIP-1679 istanbulで導入候補のEIPを整理する その4

今回も引き続き、福岡ブロックチェーン勉強会#29 エキスパートコースでQ&A形式で発表した、EIP-1679についてまとめていきます。
github.com

本記事は連載記事となっております。よろしければ前回の記事もご覧ください。
y-nakajo.hatenablog.com
y-nakajo.hatenablog.com
y-nakajo.hatenablog.com

このEIP-1679は次のEthereum1.0のアップデートであるIstanbulで導入されるEIPについてまとめたものです。EIP-1679は現在はまだ導入される候補(Proposal)EIPをリストアップしたものであり、この中から期限までに実装可能で十分にテストされたものだけが、最終的にIstanbulとしてEthereumに導入されます。
リストをみてわかる通り、多くのEIPが候補に上がっており、また似たようなEIPも多数含まれています。引き続きこれらのEIPをカテゴライズしながら紹介します。

任意の楕円曲線暗号の計算を行うプリコンパイルコントラクトを追加する提案

以下の2つのEIPが任意の楕円曲線暗号の計算処理を追加する提案になります。

EIP-1829:楕円曲線線形結合のプリコンパイル

この提案は、任意の楕円曲線について線型結合の計算を行うプリコンパイルコントラクトを追加する提案です。
現在、Ethereumでは楕円曲線暗号/署名検証のためのプリコンパイルコントラクトとして以下の4つが存在しています。

  • ecrecover: secp256k1楕円曲線の署名からaddressを復元するもの
  • bn256Add: BN256楕円曲線の2点の加算を行うもの
  • bn256ScalarMul: BN256楕円曲線のスカラ倍算を行うもの
  • bn256Pairing: BN256楕円曲線のPairing計算を行うもの

該当するGethのソースコードは以下になります。
https://github.com/ethereum/go-ethereum/blob/master/core/vm/contracts.go#L51-L60github.com

特に、下の3つはzk-SNARKsをEthereumで利用する(proofを検証する)ためのコントラクトであり、またzk-SNARKsは日々研究がされており、より計算効率の良い楕円曲線パラメータ( y^2 = x^3+ax+b \pmod{n} におけるa, b, nのこと)が見つかっています。

この提案では、新しく見つかったまたは今後見つかるであろう、zk-SNARKsで計算効率の良い楕円曲線をすぐに利用可能にするために、楕円曲線パラメータを受け取って任意の楕円曲線に関して線形結合の計算(C = s₀ ⋅ A₀ + s₁ ⋅ A₁ + ⋯ + s_n ⋅ A_n なもの)を行えるプリコンパイルコントラクトを追加します。

どうやら、最近の研究結果では楕円曲線の計算を最適化に関しては楕円曲線のパラメータは影響しないらしいということがわかってきたため、どのような楕円曲線であっても一定のgasCostで対応可能であることがこの提案の根拠となっているようです。

EIP-1962:EC演算およびPairingのランタイム定義

このEIPは上記のEIP-1829をさらに拡張するものとして提案されています。EIP-1829からの変更点として以下の3つをあげています。

  • 曲線の基底体およびスカラー体に対する任意の長さのモジュラス(ある上限まで)に対する操作
  • ペアリング操作の導入
  • 可変パラメータ長を表現できるようにABIを変更

一番大きな拡張としてはペアリング操作の追加になります。またペアリング関数の関係からEIP-1829では2^256未満の数しか扱えなかったものを任意の大きさの数を扱えるように拡張しています。また、非ペアリング操作やペアリング操作で必要となる引数が異なるため、可変長のABI定義も提案しています。
EIP-1962でサポートされるペアリング関数は以下の4つです。

  • BLS12
  • BN
  • MNT4/6
  • Cocks-Pinch法 (Limit, k <= 8) Tateペアリング?

EIP-1962が適応されることで任意の楕円曲線に対するペアリングが可能となり、プライバシーやスケーリングソリューションに必要な暗号機能が充実されると期待されています。
(*楕円曲線暗号およびペアリング関数については詳しくないので、EIPを参照ください。)

直接Ethereumノードの動作には関係しない(もしくは軽微な変更を加える)提案

ここでは、提案を導入することでEthereumノード(EVM)の動作にはそれほど影響しないが、仕様としてより明確にするための提案を解説します。
これに該当するEIPは以下の3つです。

  • EIP-1352:プリコンパイル/システム規約に制限付きアドレス範囲を指定する
  • EIP-1803:オペコードの名前をわかりやすい名前に変更
  • EIP-1985:特定のEVMパラメータに対する適切な制限

それでは個別に解説していきます。

EIP-1352:プリコンパイル/システム規約に制限付きアドレス範囲を指定する

このEIPでは0x00000000000....00ffffのアドレス(32byte中下位2byte)をプリコンパイルコントラクトのためのアドレスとして予約するという提案です。また、提案の中ではCREATEやCREATE2ではこのアドレスにアカウントが作成されるかをチェックする必要はないと明示されています(確率的に考えなくて良いから)。代わりにこれらのアドレスへのアクセスはプリコンパイルへのアクセスと見なされます。
ethereum-magiciansの議論を見るともともとEEAで提案されたもの?っぽいです。

EIP-1803:オペコードの名前をわかりやすい名前に変更

BALANCE、SHA3、NUMBER、GASLIMIT、GASおよびINVALIDの名前をよりわかりやすい名前に変えようという提案です。変更後の名前は以下のように提案されています。

  • BALANCE(0x31) -> EXTBALANCE: EXTCODESIZE、EXTCODECOPY、EXTCODEHASHと名称を一貫させるため。
  • SHA3(0x20) -> KECCAK256: SHA3アルゴリズムではないので。より明示的にアルゴリズム名をさすように。
  • NUMBER(0x43) -> BLOCKNUMBER
  • GASLIMIT(0x45) -> BLOCKGASLIMIT:トランザクションに指定されたgas limitとの混同を避けるため
  • GAS(0x5a) -> GASLEFT:残りのgas量を返すのでより明確な名前に変更
  • INVALID(0xfe) -> ABORT:「無効なオペコード」とは対照的に、誰かがこのオペコードを参照したときに明確にするため。現状、未定義のbytecodeに到達した場合や、0除算した場合などにもinvalid opcodeというエラーになっているのでそれらのエラーと区別するためのものと思われる。

これらの変更はEVMの動作を変更するものではなく、あくまでevmの実装を行なっているソースコード上の(もしくは外部のbytecodeをdecodeするツールの)ニーモニックを変更しようという提案です。

EIP-1985:特定のEVMパラメータに対する適切な制限

このEIPではEVMの動作に関して、現在の実装では暗黙的に導入されている各種パラメータの取りうる範囲を仕様としても明示的に策定しましょうという提案です。具体的には以下のような値の取りうる範囲を提案しています。

  1. Gas、GasLimit、BlockGasLimit は0〜0x7fffffffffffffff(2**63 - 1、9223372036854775807)の間の範囲とする。次のオペコードに影響する。
    • GASLIMIT(0x45)
    • GAS(0x5a)
  2. ブロック番号、タイムスタンプ は0〜0x7fffffffffffffff(2**63 - 1、9223372036854775807)の間の範囲とする。次のオペコードに影響する。
    • TIMESTAMP(0x42)
    • NUMBER(0x43)
  3. アカウントアドレス は、0〜0xffffffffffffffffffffffffffffffffffffffff(2**160 - 1、1461501637330902918203684832716283019655932542975)の間の範囲とする。つまり、アドレスは256ビット値の下位160ビットを占め、残りの上位96ビットはゼロでなければならない。次のオペコードに影響する。
    • ADDRESS(0x30)
    • ORIGIN(0x32)
    • CALLER(0x33)
    • COINBASE(0x41)
    • CREATE(0xf0)
    • CREATE2(0xf5)
  4. バッファサイズ は0〜0xffffffff(2**32 - 1、4294967295)の間の範囲とする。次のオペコードに影響する。
    • CALLDATASIZE(0x36)
    • CODESIZE(0x38)
    • EXTCODESIZE(0x3b)
    • RETURNDATASIZE(0x3d)
    • MSIZE(0x59)

これらはEVMCによって提案されており、またGethやParityなどの既存のノード実装でも一部は暗黙的に実装済みです。そのため、下位互換性は保たれた提案になります。
逆に言えば、これらの制限を超えた値を許可するようなEVM実装を持つノードが存在すると、ブロックの同期ができなくなる恐れがあります。

その他、オペコードに関係するもの

残りのEIPのうち、EVMやオペコードの挙動を変更する提案を解説します。

EIP-663:無制限のSWAPおよびDUP命令

このEIPは、現在16個先までのスタック上のアイテムしか操作できないSWAPとDUP命令に、さらに1024(スタックの最大サイズ)までの任意の値を指定して操作可能なSWAPn、DUPn命令を追加する提案です。それぞれのオペコードの提案内容は以下の通りです。

  • DUPn(0xb0):スタックの先頭からアイテムを1つ取り出し(nとする)、nの場所のスタックアイテムをスタックの先頭にコピーする
  • SWAPn(0xb1):スタックの先頭からアイテムを1つ取り出し(nとする)、nの場所のスタックと先頭のスタックアイテムを入れ替える

なお、どちらのオペコードもnが1024以上の場合および、nが現在のスタックサイズよりも大きい場合はスタックアンダーフローエラーを発生させなければいけません。

これらのオペコードの追加により、より深いスタックの操作がより少ないオペコードで可能になります。

EIP-1930:ガスが不足時にはRevertする厳密なガスセマンティクスを持つCALL

このEIPはタイトルの通り、厳密にgas leftと要求されるgasをチェックし、gas leftが要求されるgasよりも少ない場合はCALLを呼び出す前に強制的にrevertエラーを発生させるCALLセマンテクスを追加する提案です。

現在のCALLオペコードは以下のインターフェースを持っています。

let sucess = CALL(gas, addr, value, argsOffset, argsLength, retOffset, retLength)

このインターフェースからわかる通り、CALLオペコードでは呼び出す際に消費する”最大”gas量(言い換えれば、CALL先に渡す”最大”gas量)を指定できます。しかし、これはあくまで、”最大”のgas量のため、例えば、CALLで指定されているgasが100,000だが、CALLオペコード実行時の残りのgas量が98,000だった場合は98,000のみがCALL先に渡されます。さらに、DoS攻撃対策として導入されたEIP-150により、CALLに渡されるgas量には63/64ルールが適応されるため、実際には98,000ではなく96,468が渡されます。

現状のCALLの問題点は呼び出し先のCALLが消費するgas量が一定であり、かつ呼び出し元の残りgas量が足りていない場合に必ず「out of gas」が発生してしまい、全てのgasが消費されてしまうことです。
この問題は特にCALL先が予想できないmeta-transaction(EIPではGnosis Safeなどが例に挙げられている)で発生する(している)とEIP内で指摘されています。

また、別の例としてEIP-165のようなシンプルな連携機能の呼び出しでも問題が発生すると指摘されています。
EIP-165はそのコントラクトがなんのインターフェースを実装しているか?というのを調べるための共通仕様となっています。EIP-165ではコントラクトが実装しているインターフェースを調べるために「supportsInterface」を呼び出すことを規定しており、この関数を実行するためには30,000gasを消費するということも規定しています。しかし、現在のCALLの仕組み上30,000gasに満たないgasしか残っていない場合はこの呼び出しは失敗します。その結果、呼び出し元のコントラクトは対象のコントラクトが指定されたインターフェースを実装していないと誤認してしまう恐れがあります。

このような状況でも、呼び出し前に厳密なgasチェックを行うことで、呼び出した結果失敗したのか、呼び出した結果失敗したのかがわかりやすくなるため、より効率よく検証が行えるようになります。

残りの提案

残っているEIPについて個別に解説していきます。

EIP-1057:ProgPoW、プログラマティックProof-of-Work

このEIPは現在Ethereumで利用されているPoWのアルゴリズムをEthashからProgPoWという新しいアルゴリズムに変更するという提案です。ProgPoWの詳しい仕様については理解しておりませんので説明は割愛します。特徴としてはEthashがASICで2倍の速度が得られるのに対して、ProgPoWでは1.1〜1.2倍程度しか速度の向上が行えず、よってEthashより高いASIC耐性があるとEIP内で説明されています。

EIP-1559:ETH 1.0チェーンのための手数料市場の変更

このEIPは現在のファーストプライスオークション形式の手数料市場を変更しようという提案です。
現在のファーストプライスオークション形式では以下のような問題があると指摘されています。

これらの問題を解消するために以下のように手数料市場を変更します。

  • 800万gasを基準のblock gas limitとし、gasの需要(つまりトランザクションの需要)に応じてBASEFEEを変更する。
  • BASEFEEはマイナーの報酬とはならずBurnされる。これによりマイナーがBASEFEEを釣り上げるインセンティブはなくなる。
  • BASEFEEを超えたpriceはpremium_priceとなり、その分だけがマイナーの報酬になる。(EIP内ではチップと表現されてる)。

より詳細な仕様についてはEIPを参照ください。

個人的には、BASEFEEを超えた分が(少額とはいえ)マイナーの手数料になるので、現在のファーストプライスオークション形式とあんまり変わらない気がしています。しかもマイナーはBASEFEEを引き上げない方が報酬が増えて嬉しいので、トランザクションをブロックにあまり入れない方がよく、現状よりも悪い結果になりそうな気もします。
このEIPについてはあまり理解できてないので、自分の理解が間違っていたらぜひ教えてください。(tx.gasprice // 2**128ってtx.gasprice^(-2^128)ってことなのかな?だとしたら必ず0になっちゃう気が。。。?)

EIP-1702:一般的なアカウントバージョニングスキーム

このEIPではAccount Stateに「version」フィールドを追加することで、アカウントにバージョンの概念を追加しようという提案です。これにより、スマートコントラクトを異なるバージョンのEVMで実行することなどが可能になります。

この提案が導入された後にデプロイされたコントラクトは全て「version」ステートを持ちます。また、この提案では将来的なアカウントのバージョンアップも考慮されており、あるアカウントのバージョンNが保有するステートの個数(=length)をチェックする仕組みなども定義されています。

EIP-1702EIP-615の前提となっており、EVMのバージョンアップに対してより効力を発揮する(複数のEVMバージョンを混在した形で実行可能になるなど)提案になります。
また、State Rentに関係するアップデートの中にもアカウントのステートを追加するような提案が含まれているものもあり(最近の議論ではそれらはスマートコントラクト側に記録するように変更されたようですが)、これらの提案を含む今後の広い範囲のアップデートもカバーできるような提案となっています。

EIP-2014:拡張状態オラク

このEIPはプリコンパイルコントラクトとして、外部情報を共有(set, get)するコントラクトを追加しようという提案です。これ以上は特に説明することはないですw

ラクルはそもそもとしてその情報の正しさを証明できないので、プリコンパイルコントラクトを用意してまで対応するようなものではないかなって思います。

まとめ

少し遅刻しましたがなんとかistanbulの候補に上がっているEIPを全て解説できました。
先日7/20のcore devs callでEIP-2028が採択されたりと動きが出てきてますね。
istanbulに実際に採用されるものが確定するまではもう少し時間がかかりますが、istanbulでどのように変わっていくのか?を確認する助けになれば幸いです。