Author: Gimer Cervera, Ethereum Smart Contract Developer Translated by Good Oba, Golden Finance
Introduction
この記事では、スマートコントラクトの最適化とセキュリティのためのイーサネット仮想マシン(EVM)とSolidityアセンブリを掘り下げる一連の記事を紹介します。
イーサネットバーチャルマシン(EVM)は、イーサネットネットワークのコアコンポーネントです。EVMは、Solidityのような高級言語で書かれたスマートコントラクトのデプロイと実行を可能にするソフトウェアの一部です。EVMは、Solidityのような高水準言語で書かれたスマートコントラクトのデプロイと実行を可能にするソフトウェアの一部である。いったんコントラクトが書かれると、それはバイトコードにコンパイルされ、イーサネットネットワークの各ノード上で実行されるEVMにデプロイされる。
Solidityアセンブリは低レベルのプログラミング言語で、開発者はEVMにより近いレベルでコードを書くことができます。スマートコントラクトの実行をよりきめ細かく制御し、高レベルのSolidityコードだけでは不可能な最適化やカスタマイズを可能にします。
Solidityのインラインアセンブリに使用される言語はYulと呼ばれ、このプログラミング言語はEVMバイトコードにコンパイルするための仲介役として機能します。これは、開発者がスマートコントラクトの実行をより細かい粒度で制御できるようにする低レベル言語として設計されています。Yulは低レベルのスタックベースの言語として設計されており、開発者はより最適化された効率的なコードを書くことができます。Solidity のアセンブルを説明する前に、EVM のコンポーネントがどのように機能するかを理解する必要があります。
EVMは準チューリング完全ステートマシンです。この場合、"quasi"という用語は、プロセスの実行が、任意のスマート・コントラクトの実行に利用可能なガスの量に応じて、有限の計算ステップ数に制限されることを意味します。これは、イーサネットが停止問題や、実行が(悪意を持って、または偶発的に)永遠に実行される可能性がある状況を処理する方法です。これにより、イーサプラットフォームの完全な麻痺を避けることができます。
ガスは、イーサでトランザクションを完了するために必要な計算量を測定する概念です。取引コストはイーサで支払われ、ガスとガスの価格に関連しています。このプロセスにおける私たちの目標は、セキュリティを損なうことなく、消費されるガスの総量を最小限に抑える方法を学ぶことです。
コードの最適化の問題
インラインアセンブリは、低レベルでEVMにアクセスする方法です。Solidity のいくつかの重要なセキュリティ機能とチェックをバイパスします。インラインアセンブリを適切に使用すると、実行コストを大幅に削減できます。しかし、インラインアセンブリは必要なタスクにのみ使用し、自分が何をしているかを理解している場合にのみ使用してください。コードを最適化するためにインラインアセンブリを使用すると、コードに新たなセキュリティ問題が発生する可能性があります。インライン・アセンブリを使いこなすには、EVMとそのコンポーネントがどのように機能するかを理解する必要があります。
EVMでは、ストアド変数に初めてアクセスするたびに、"cold"アクセスと呼ばれる2,100ガスを支払う必要があります。"ホット"アクセスと呼ばれ、100ガスを消費します。
次のコードは、Yulを使用してコードを最適化する方法の例です。関数 SetData1 は、Solidity を使用する従来の方法で、グローバル変数 value に新しい値を設定します。 この新しい値を最初に代入するときは 22514 ガスがかかります。
Introduction
この記事では、以下の一連の記事を紹介します。スマートコントラクトの最適化とセキュリティのためのイーサネット仮想マシン(EVM)とSolidityアセンブリを詳しく見ていきます。
イーサネット仮想マシン(EVM)はイーサネットネットワークの中核コンポーネントであり、Solidityのような高級言語で書かれたスマートコントラクトのデプロイと実行を可能にするソフトウェアの一部です。EVMは、Solidityのような高水準言語で書かれたスマートコントラクトのデプロイと実行を可能にするソフトウェアの一部である。いったんコントラクトが書かれると、それはバイトコードにコンパイルされ、イーサネットネットワークの各ノード上で実行されるEVMにデプロイされる。
Solidityアセンブリは低レベルのプログラミング言語で、開発者はEVMにより近いレベルでコードを書くことができます。スマートコントラクトの実行をよりきめ細かく制御し、高レベルのSolidityコードだけでは不可能な最適化やカスタマイズを可能にします。
Solidityのインラインアセンブリに使用される言語はYulと呼ばれ、プログラミング言語はEVMバイトコードにコンパイルするための仲介役として機能します。これは、開発者がスマートコントラクトの実行をより細かい粒度で制御できるようにする低レベル言語として設計されています。Yulは低レベルのスタックベースの言語として設計されており、開発者はより最適化された効率的なコードを書くことができます。Solidity のアセンブルを説明する前に、EVM のコンポーネントがどのように機能するかを理解する必要があります。
EVMは準チューリング完全ステートマシンです。この場合、"quasi"という用語は、プロセスの実行が、任意のスマート・コントラクトの実行に利用可能なガスの量に応じて、有限の計算ステップ数に制限されることを意味します。これは、イーサネットが停止問題や、実行が(悪意を持って、または偶発的に)永遠に実行される可能性がある状況を処理する方法です。これにより、イーサプラットフォームの完全な麻痺を避けることができます。
ガスは、イーサでトランザクションを完了するために必要な計算量を測定する概念です。取引コストはイーサで支払われ、ガスとガスの価格に関連しています。このプロセスにおける私たちの目標は、セキュリティを損なうことなく、消費されるガスの総量を最小限に抑える方法を学ぶことです。
コードの最適化の問題
インラインアセンブリは、低レベルでEVMにアクセスする方法です。Solidity のいくつかの重要なセキュリティ機能とチェックをバイパスします。インラインアセンブリを適切に使用すると、実行コストを大幅に削減できます。しかし、インラインアセンブリは必要なタスクにのみ使用し、自分が何をしているかを理解している場合にのみ使用してください。コードを最適化するためにインラインアセンブリを使用すると、コードに新たなセキュリティ問題が発生する可能性があります。インライン・アセンブリを使いこなすには、EVMとそのコンポーネントがどのように機能するかを理解する必要があります。
EVMでは、ストアド変数に初めてアクセスするたびに、"cold"アクセスと呼ばれる2,100ガスを支払う必要があります。"ホット"アクセスと呼ばれ、100ガスを消費します。
次のコードは、Yulを使ってコードを最適化する方法の例です。関数 SetData1 は、Solidity を使用する従来の方法で、グローバル変数 value に新しい値を設定します。最初にこの新しい値を代入するときは22514ガスがかかります。

関数setData2はインラインアセンブリを実装しています。インライン アセンブリ ブロックは、アセンブリ { ... } でマークされ、中括弧内のコードはユール言語のコードです。この時点でソースコードを知る必要はない。ただ、ソフトウェアがより低いレベルでストレージにアクセスしていることを覚えておいてほしい。その結果、実装コストは低くなる。
この例では、最初に値が変更されるときには22484ガスがかかります。 この差は大きくないと思われるかもしれませんが、このコードが何千回も実行される可能性があることを考慮する必要があります。

ストレージはなぜ高価なのでしょうか?データが1か所だけでなく、何万ものノードに保存される分散型の世界であることを思い出してください。また、将来のトランザクションでアクセスや変更が必要になった場合、ネットワーク内のすべてのノードが簡単に利用できなければなりません。そのデータの全体的なコストは、それが消費するストレージスペースと、ネットワーク全体でそのデータを生成するのにかかる計算量の合計に等しい。
EVMのスタック、ストレージ、メモリ
EVMはスタックベースのマシンで、スタックと呼ばれるデータ構造上で動作し、値を保存して演算を実行します。EVMは、ストレージの読み書き、他のコントラクトの呼び出し、数学演算などのタスクを実行するために使用される独自の命令セット(オペコードと呼ばれる)を持っています。スタックはLast In First Out (LIFO)方式で動作する(図1を参照)。これは、最近挿入されたアイテムがスタックの最上位に格納され、最初に削除されるアイテムであることを意味する。

スマートコントラクトを実行する際、EVMはさまざまなデータ構造と状態変数を含む実行コンテキストを作成します。実行が完了すると、実行コンテキストは破棄され、次のコントラクトに備えます。実行中、EVMはトランザクション間で持続しない一時的なメモリーを維持する。EVMは1024項目の深さを持つスタックマシンを実行する。各アイテムは256ビットのワードであり、このサイズは256ビットのハッシュと楕円曲線暗号の使用を容易にするために選択された。
EVMには以下のコンポーネントがあります(図2を参照)。
スタック:EVMのスタックはデータオンザフライ(LIFO)マシンです。スマートコントラクトの実行中に一時的な値を保存するために使用されるLIFO)データ構造です。
ストレージ:恒久的なストレージで、イーサの状態の一部であり、最初に初期化されたときだけゼロになります。
メモリ:揮発性で、契約実行中に中間データを格納するために使用されるバイトの動的なサイズの配列。メモリは、新しい実行コンテキストが作成されるたびにゼロに初期化される。
カルデータ(Calldata):これも揮発性のデータ記憶領域で、メモリに似ています。ただし、不変のデータを保存します。スマートコントラクトのトランザクションの一部として送信されたデータを保持するように設計されています。
プログラムカウンタ:プログラムカウンタ(PC)は、EVMによって実行される次の命令を指す。
仮想ROM:スマートコントラクトは、この領域にバイトコードとして格納されます。仮想ROMは読み取り専用です。

EVMスタック
このアーキテクチャでは、プログラムの命令とデータはメモリ上に保持されます。このアーキテクチャでは、プログラムの命令とデータはメモリに保持され、プログラムの実行はスタックの先頭を指すスタックポインタによって制御されます。スタック・ポインタは、次の値や命令がスタックのどこに保存されるのか、あるいはどこに取り出されるのかを追跡します。プログラムが実行されると、スタックに値が追加され、すでに存在する値に対して演算が行われる。例えば、2 つの数値を足したい場合、スタックに数値を入力し、上の 2 つの数値に加算演算を行う。そして結果がスタックに戻される。

スタックベースのアーキテクチャの最も重要な特徴の1つは、非常にシンプルで効率的な操作の実行を可能にすることです。スタックはLIFOデータ構造であるため、データと命令を簡単かつ迅速に処理することができます。
EVMには、オペコードと呼ばれる独自の命令セットがあります。オペコードは、ストレージの読み書き、他のコントラクトの呼び出し、数学演算の実行などのタスクを実行するために使用されます。
スタック操作:POP、PUSH, DUP, SWAP
Arithmetic/Comparison/Per-position: ADD, SUB, GT, LT, AND, OR
Environment:CALLER, CALLVALUE, NUMBER
メモリー操作: MLOAD, MSTORE, MSTORE8, MSIZE
ストア操作:SLOAD、SSTORE
プログラムカウンタ関連のオペコード:JUMP、JUMPI、PC、JUMPDEST
プログラムカウンタ関連のオペコード:MLOAD、MSTORE8、MSIZE
Stop opcodes: STOP, RETURN, REVERT, INVALID, SELFDESTRUCT
EVM storage
EVMストレージは、256ビットのキーと値のペアを保持する不揮発性スペースです。コントラクトのストレージスロットの総数は2²⁵⁶で、これは非常に大きなスロット数です。ブロックチェーン上の各スマートコントラクトは、それぞれのストレージスペースを持っています。
ストレージは、関数呼び出しの間に記憶する必要があるデータのために、関数呼び出しの間に使用されます。スマートコントラクトの実行が終了した後でも利用可能である必要がある変数やデータ構造を保存するために使用されます。

ストレージへのアクセスは、オペコードSLOADとSSTOREによって与えられます
。このアカウントのストレージは永続的なデータストレージであり、スマートコントラクトによってのみ使用されます。外部所有アカウント(EOA)は常にコードがなく、空のストレージを持ちます。
EVMメモリ
メモリはアーキテクチャ内の揮発性メモリであり、そのデータはブロックチェーン内で永続的ではありません。インメモリはランダムアクセスのデータ構造で、スマートコントラクトの実行中に一時的なデータを保存します。

メモリは4つのセクションに分けられます:ステージングスペース用の2つのスロット、空きメモリポインタ用の1つのスロット、利用可能な空きメモリを指す0と1のスロットです。最初の64バイトの領域はハッシュメソッドによって使用され、最終的な出力を返すまで、中間出力を保存するための一時的な領域が必要となる。
空きメモリポインタは、単に空きメモリの開始を指すポインタです。これはスマート・コントラクトが、どのメモリ・ロケーションに書き込まれ、どれがまだ利用可能かを追跡することを保証する。これにより、コントラクトが別の変数に割り当てられたメモリを上書きするのを防ぐことができる。図6は、メモリがどのように分割されるかを示している。

メモリは、メモリに保持する必要のない変数やデータ構造の保存に使用されます。メモリのサイズはスマートコントラクトの実行中に調整できますが、アクセスはスタックよりも遅く、コストがかかります。
メモリはゼロ初期化され、メモリにアクセスするために使用されるオペコードはMLOAD、MSTORE、MSTORE8
全体
この記事では、イーサネット仮想マシン (EVM) に関連する基本概念のいくつかを確認しました。インライン・アセンブリ・コードを実装するには、EVM を深く理解する必要があります。これは、EVM のコンポーネントの一部と相互作用するためです。さらに、バイトコード、Gas、Application Binary Interface (ABI)などの重要な概念についても復習します。最後に、スマートコントラクトの実行を安全に最適化するために、オペコードがどのように機能するのか、さらにインラインアセンブリの例について説明します。