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

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

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

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

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

新しいOPCODEを追加する提案

EIP-615:EVMのためのサブルーチンとスタティックジャンプ

このEIPは過去の記事で解説していますのでそちらをご覧ください。
y-nakajo.hatenablog.com

chainIDを取得するOPCODEを追加する提案

これは以下の3つのEIPで提案されています。

  • EIP-1344:ChainIDオペコードの追加
  • EIP-1959:chainIDがchainIDの履歴の一部であるかどうかをチェックするための新しいOPCODE
  • EIP-1965:chainIDが特定のブロック番号で有効かどうかを調べる方法

このEIPは主にLayer2のためのものです。例えばmeta-transactionのケースを考えます。off-chainで発行されたmeta-transactionがどのネットワーク(mainnet、ropsten、kovanなど)で実行されるべきかを指定するためにmeta-transactionの署名にchainIDを含めます(EIP-155フォーマットなど)。しかしながら、コントラクトでは自身がどのchain上で実行されているのかを知るすべがありません。そのため、原則的にはmeta-transactionはリプレイアタックに脆弱であるという問題があります。
このEIPを導入するとこの問題が解決され、meta-transactionのリプレイアタックを防ぐことが可能になります。

これら3つのEIPの違いは以下の通りです。

  • EIP-1344では単純に今現在のchainIDの値を取得します。このEIPの問題はchainが分岐し、chainIDが変更された場合にそれ以前に作成されたmeta-transactionが無効になってしまうことです。
  • EIP-1959では上記の問題を解決するために、chainIDを引数として指定すると、genesis blockから今までに変異してきたchainIDのどれかと一致すれば1を返し、どれにも一致しない場合は0を返すというOPCODEの提案です。この提案でも1つ問題があります。以下に例を示します。
ある時点から分岐したchainをchainA、chainBがある。
分岐前のchainIDは1である。
ここで、chain分岐によってそれぞれのchainIDが次の様に変更されます。
chainA -> 1 (変更なし)
chainB ->1001 (1から1001に変更)
上記の場合は、chainAに対して発行したmeta-transactionがchainBでも有効となりリプレイアタックが成功してしまいます。
  • EIP-1965では、さらにEIP-1959の問題を解決します。EIP-1965ではOPCODEの引数として、chainIDとblockNumberをとります。これにより、指定されたblockNumberでそのchainIDと一致するかどうかを検証可能となり、上記EIIP-1959の問題が解決されます。

上記の説明を読むと、EIP-1965を導入すれば全て解決と思われますが、本来、そんなにchainの分岐というのは頻繁に発生すべきものではないと考えられています。その点からいえば、EIP-1965は変更範囲が大きすぎるとも言えるため、最適な変更を議論するためにこれら3つのEIPが候補に入っていると思われます。(単に実装の複雑さがEIP-1344 < EIP-1595 < EIP-1965なので、実装が間に合ったやつだけ入れようかなっていうだけな気もしますw)

Precompiled Contractのgas costを下げる提案

Precompiled Contractとは、署名検証のための演算処理など、コントラクトから利用できると非常に有用ではあるが、コントラクトとして実装すると莫大なgas costがかかってしまう処理に関して、node実装の中にあらかじめ組み込まれている(つまりnative compileされ高速に動作可能となっている)コントラクトのことです。ここではPrecompiled Contractを呼び出す時に余分に消費されるgas costを削減する提案についてまとめます。

EIP-1108:alt_bn128Precompiled Contractのgas costの削減

これはzk-SNARKsのproof検証に使われる演算処理のためのPrecompiled Contractです。これらの処理(EC_MUL, EC_ADD, Paringの3つ)のアルゴリズムは随時最適化されており、導入された当初よりも高速に演算が可能となっています。そのため、この提案では現在の高速・最適化された処理に合わせてgas costも削減しようという提案です。

Precompiled Contractのcall費用を削減する提案

現状、Precompiled Contractは通常のコントラクトと同様にSTATICCALL OPCODEを介して呼び出されます。この時、STATICCALL OPCODE自体のgas costとして700が消費され、さらに呼び出すPrecompiled Contractごとに追加のgas costが消費されます。(Precompiled Contractのgas costは基本的に高めに設定されています。)
Precompiled Contractを呼び出す時に使用されるSTATICCALLのgas costを削減する意味は計算コストとgas costの不整合を是正するためです。現状Precompiled Contractの計算コストがどんなに低くても、CALLのgas costである700が必ず消費されてしまいます。これを是正することがこれらの提案の目的です。
以下の2つの提案がPrecompiled Contractを呼び出す時にかかるgas costを低減する提案になります。

  • EIP-1109:PRECOMPILEDCALL命令コード(プリコンパイルされた契約のCALL費用を削除)
  • EIP-2046:Precompiledに対するSTATICCALLのgas costの削減

これら2つの違いは以下の通りです。

  • EIP-1109はPrecompiled Contractを呼び出す新しいOPCODE、PRECOMPILEDCALLを追加する提案です。このOPCODE自体にはgas costが存在せず、呼び出すPrecompiled Contractに設定されたgas costのみが消費されます。(つまり700分のgas costが削減されます。)
  • EIP-2046はSTATICCALL OPCODEのgas costを実行時に変化させる提案です。STATICCALLで呼び出すコントラクトの対象がPrecompiled Contractの場合はgas costを40に、それ以外の場合は今まで通り700にするという提案です。これにより、今までより安価にPrecompiled Contractを利用することが可能になります。

EIP-1283:ダーティーマップを使用しないSSTOREのネットガス計測

(2019/07/17修正: EIP-1930と紹介していたが、EIP-1706の間違いだったので訂正しました。)これは前回のアップデート、Constantinopleで延期されたEIPになります。Istanbulでは前回の問題点を改善するためのEIPと一緒に導入される可能性があります。ここではこれらの2つのEIPについて説明します。

  • EIP-1283:ダーティーマップを使用しないSSTOREのネットガス計測
  • EIP-1706:通話料よりも低いgasleftの場合はSSTOREを無効にする

EIP-1283については、以前にGBECの動画で解説したのでそちらを参照ください。
goblockchain.network

EIP-1706はEIP-1283で発生してしまう、リエントランシーバグを解消するための提案です。具体的には、SSTORE(Storageにデータを書き込むOPCODE)を呼び出す際に、残りのgasが5000未満であれば強制的に失敗とみなすというものです。

EIP-1283を導入すると、条件によってはSSTOREのgas costが通常、最低5000であるのに対して、最低200まで大幅に削減されます。しかし、この大幅な値下げにより、リエントランシーバグが発生してしまいます。そのため、EIP-1706では、SSTORE実行時のgas costが200しか消費されない場合でも、5000以上のgasを持っていない場合は実行失敗にするという提案です。これにより、リエントランシーの発生を防ぐことができます。

まとめ

内容が非常にボリューミーとなってしまったので今回の解説はここまでです。
EIP-1679については連載記事として引き続き解説していきます。次の記事も近いうちに公開したいと思います。