イーサリアムバーチャルマシン(EVM)の欠陥と欠点(中)

本記事は全3回イーサリアムバーチャルマシン(EVM)の欠陥と欠点の第2回記事です。

第1回はこちら。
イーサリアムバーチャルマシン(EVM)の欠陥と欠点(前)

*わたしは非技術者なので翻訳には誤りがあると思います。もし内容や表現に誤りがあれば教えていただけると幸いです。

EVMのスタック

EVMはスタックベースのマシンです。つまり、一連のレジスタではなくほとんどの操作でスタックを使用します。スタックベースのマシンは、通常、最適化する方がはるかに単純ですが、同様のレジスタベースのマシンと比較して、ほとんどのオペレーションではより多くのオペコードが必要になります。

とにかく、EVMにはさまざまな操作があります。そのほとんどはスタックだけで動作します。 SWAPおよびDUPシリーズの指示に注意してください。これらは最大16になります。今このコントラクトをコンパイルしてみてください。

pragma solidity ^0.4.13;

contract Something{

    function foo(address a1, address a2, address a3, address a4, address a5, address a6){
        address a7;
        address a8;
        address a9;
        address a10;
        address a11;
        address a12;
        address a13;
        address a14;
        address a15;
        address a16;
        address a17;
    }
}

あなたはこのエラーに出会うことでしょう。

CompilerError: Stack too deep, try removing local variables.

このエラーは、アイテムがスタック内で16レベル深くなると、スタックから項目をポップすることなくアクセスすることは事実上不可能であるために発生します。この問題の正式な「解決策」は、変数を少なくして関数を小さくすることです。さまざまな回避策には、構造体または配列に変数を詰め込み、memoryキーワードを使用することも含まれます(とある理由のために通常の変数には適用できません)。だから、いくつかのメモリベースの構造体を使用するコントラクトを修正することができます。

pragma solidity ^0.4.13;

contract Something{
    struct meh{
        address x;
    }
    function foo(address a1, address a2, address a3, address a4, address a5, address a6){
        address a7;
        address a8;
        address a9;
        address a10;
        address a11;
        address a12;
        address a13;
        meh memory a14;
        meh memory a15;
        meh memory a16;
        meh memory a17;
    }
}

このコントラクトをコンパイルすると結果はこうなります。

CompilerError: Stack too deep, try removing local variables.

これらの変数をメモリに置き換えましたか?修正しないのですか?うーん、ダメです。これはスタックに17個の256ビット整数を格納する代わりに、256ビットのメモリスロットに13個の整数と4個の256ビットメモリアドレス(すなわちレファランス)を格納するためです。これはSolinityの問題ですが、主な問題はEVMがスタック上の任意の項目にアクセスする方法がないことです。私が知っている他のすべてのVM実装は以下を含みます。

  1. 小さなスタックサイズを奨励し、スタックアイテムをメモリや別のストレージに簡単にスワップする(.NETのローカル変数など)
  2. 任意のスタックスロットへのアクセスを許可するpick命令などの実装

しかし、EVMでは、スタックはデータと計算のためのメモリの唯一の自由な場所ですが、他の場所はガスの形で直接コストがかかります。

だから、これは小さなスタックサイズを直接失望させます。なぜなら、他の場所ではもっと高価だからです。こうして、基本的な言語実装の問題に遭遇します。

バイトコードサイズ

理論的なドキュメントでは、EVMバイトコードの目的はシンプルでコンパクトであると述べています。しかし、これは説明的で簡潔なコードを書くことを好むと言っているようなものです。彼らは根本的に異なる目標を達成しています。簡単な命令セットは、操作の数を制限し、操作を簡潔かつ簡潔に保つことによって達成されます。一方、小さなプログラムを生成するコンパクトなバイトコードは、可能な限り数バイトのコードで、できるだけ多くの操作を実行する命令セットを作ることによって達成されます。

結局のところ、 “コンパクトなバイトコードサイズ”が目標であるにもかかわらず、EVMの実装ではその目標を達成できません。

代わりに、ガスモデルを簡単に作成できる簡単な命令セットに焦点を当てています。私はこれが間違っているとか悪いと言っているわけではなく、EVMの主な目標の1つが基本的にEVMの他の目標によって達成できないことを言いたいのです。

また、その文書で与えられている1つの数字は、 “hello world”を実装するためにC言語が4000バイト以上を要するということです。これは間違いなく、C言語で行われているさまざまな環境や最適化にも当てはまります。彼らが測定したC言語では、ELFデータ、再配置データ、アライメントの最適化があると予想しています。32バイトや4キロバイトなどの特定の境界にコードとデータを並べると、物理プロセッサ上のプログラムのパフォーマンスに測定可能な影響があります。

私はx86マシンコード46バイトにコンパイルする単純なC言語と、700バイトまでにコンパイルするシンプルなグリータータイプのプログラムを作成しました。Solidityのサンプルは1000バイトを超えるEVMバイトコードにコンパイルします。

私は、セキュリティ上の理由から単純な命令セットの必要性を理解していますが、それはブロックチェーンに肥大化を引き起こします。

あたかもEVMスマートコントラクトのバイトコードが可能な限り小さいかのようにこれを渡すことは有害であると思います。このようなことのためにいくつかのオペコードを実行する必要はなく、一般的な操作のバッチを実行する標準ライブラリとサポートするオペコードを含めることで、はるかに小さくすることが可能なのです。

256ビットの整数問題(再び)

しかし実際、256ビットの整数はひどいです。そして最もばかげた部分は、合理的な使用がない場所で使用されることです。 4B(32ビット)以上のガスを使用することは事実上不可能です。

ガスの特定と計数にどのような整数が使用されますか?もちろん256ビットです。

メモリはかなり高価ですので、EVMのメモリアドレスのアドレスサイズはどのくらいですか?あなたのコントラクトが宇宙の原子より多くの記憶の言葉を必要とするときには、もちろん256ビットでいいでしょう。私は、永久記憶域のアドレスと値の両方に256ビットの整数を使用することについて否定していますが、実際にはいくつかのデータにハッシュを使用し、アドレス空間の競合についての心配はありません。任意の整数サイズを使用できるすべてのインスタンスでは、EVMは256ビットを呼び出します。 JUMPでも256ビットを使用しますが、防御では最高ジャンプ先を0x7FFFFFFFFFFFFFFFに制限し、ジャンプ先を符号付き64ビット整数に制限します。そして、それが通貨価値そのものです。

ETHの最小単位はweiなので、総貨幣供給量(wei)は1000000000000000000 * 200000000(200Mは概算で、現在の供給量は92M)です。そして、その数を2から減算すると256の累乗(256ビット整数で保存可能な最大値)は、1.157920892373162e + 77となります。宇宙の原子の数よりも大きな大きさを加えて、存在するよりも多くの宇宙を送るのに十分な空間です。

基本的に、256ビットの整数は、EVMが設計されているほとんどのアプリケーションでは、実用的ではなく、不要でしょう。

(次回記事に続きます)

スポンサーリンク