[EIP140] REVERT命令の仕組みいついて理解しよう！

# はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回は、コントラクトでエラーが起きた時、実行を中止して原因を返す仕組みを提案している規格である**EIP140**についてまとめていきます！

:::note info
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-140

他にも様々なERCについてまとめています。

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

# 要約

`REVERT`命令は、すべてのガスを消費することなく実行を停止し、状態変更を戻してエラーの原因を返すことができます。

# 概要

「`REVERT`」は主にイーサリアムのスマートコントラクトで使用される機能です。
スマートコントラクトは、ブロックチェーン上で自動的に実行されるプログラムのことを指します。
`REVERT`命令の役割は、スマートコントラクトの実行中に何らかのエラーが発生した場合、それまでに行われたすべての状態変更を元に戻し（ロールバックし）、エラーコードやメッセージを指すメモリ領域へのポインタを提供することです。

:::note info
`REVERT`命令が実行されると、スマートコントラクトの実行は中断され、それまでのトランザクションによって変更されたすべてのデータが元の状態に戻されます。
しかし、このプロセスで重要なのは、全てのガス（トランザクションを実行するために必要な手数料のようなもの）が消費されないという点です。
つまり、`REVERT`命令はエラーが発生した時に、一部のガスを節約しつつトランザクションの失敗を扱う方法を提供します。
:::

この機能は、スマートコントラクトの安全性を高め、不必要なガスの消費を防ぐために非常に重要です。
例えば、**ERC20**や**ERC721**などの標準規格を実装するスマートコントラクトでよく使用されます。

:::note info
**ERC20**については以下を参考にしてください。
:::

https://chaldene.net/erc20

:::note info
**ERC721**については以下を参考にしてください。
:::

https://chaldene.net/erc721

# 動機

現在のイーサリアムのスマートコントラクトでは、コントラクト内からトランザクションを元に戻す（`revert`する）ことは基本的に不可能です（この規格が提案された当時は）。
トランザクションをリバートする実用的な方法は二つありますが、どちらも全ての残りのガスを消費します。

### ガス切れ
トランザクションの実行中にガス（トランザクションを実行するための手数料）が尽きることです。
ガスが尽きると、トランザクションは自動的にリバートされます。

### 無効な命令の実行
コントラクトが無効またはエラーを含む命令を実行すると、トランザクションはリバートされます。

さらに、イーサリアム仮想マシン（EVM）の実行をリバートすると、すべての変更（ログを含む）が失われます。
その結果、EVM実行を中止する原因を伝える方法がありません。

これは、特に**ERC20**や**ERC721**などの標準規格を使用するコントラクトにおいて重要です。
これらのコントラクトではセキュリティと効率が非常に重視されるため、実行のリバートが難しいという特性を理解することが重要です。

# 仕様

イーサリアムの`BYZANTIUM_FORK_BLKNUM`以降のブロックでは、新しい命令`REVERT`が導入されました。
この`REVERT`命令は、スマートコントラクトの実行を中止するために使用されます。
具体的には、スタックの上から二つの要素、`memory_offset`と`memory_length`を必要とします。
これらは、エラーメッセージを指定するために使われます。

`REVERT`命令は以下のように動作します。

### 実行の中止とロールバック
実行が停止され、それまでの状態変更（データの更新など）が元に戻されます（ロールバックされます）。

### エラーメッセージの提供
指定されたメモリ領域からエラーメッセージが取得され、呼び出し元に渡されます。
これは`RETURN`命令と同様の方法で処理されます。

### コスト
`REVERT`命令のコストは`RETURN`命令と同じで、ロールバック自体は全ガスを消費しません。
コントラクトはメモリの使用に対してのみ料金を支払います。

### ガス不足の場合
十分なガスがないか、スタックアンダーフローが発生した場合、`REVERT`命令は通常のガス切れ例外と同じように全ガスを消費します。

### `CREATE`や`CREATE2`の文脈での使用
`REVERT`が`CREATE`や`CREATE2`コマンドと一緒に使われる場合、コードはデプロイされず、スタックには`0`が置かれ、エラーメッセージは利用可能です。

なお、提供されるメモリセクションの内容については、このEIP（Ethereum Improvement Proposal）では定義されていませんが、将来的に別のEIPで扱われる可能性があります。
これは、**ERC20**や**ERC721**などの標準規格に基づくコントラクトの安全性と効率を高めるために重要な機能です。

# 後方互換性

この変更は、`0xfd`を命令として含まない限り、過去に作成されたコントラクトには影響しません。

# テスト

```
6c726576657274656420646174616000557f726576657274206d657373616765000000000000000000000000000000000000600052600e6000fd
```

上記のコードはイーサリアムのスマートコントラクトで使用されるバイトコードの一部です。

### REVERTデータの返却
このコードは`REVERT`命令を使用しています。
`REVERT`は実行を中止し、特定のエラーメッセージを返します。
この場合、返されるべきエラーメッセージは`0x726576657274206d657373616765`です。
これは、バイトコード内の指定された位置にあるデータを意味します。

### ストレージの未設定
このコードの実行により、ストレージのキー`0x0`の位置にあるデータは変更されず、未設定のまま残ります。
これは`REVERT`命令が状態変更をロールバックするためです。

### ガスの消費量
このコードの実行には合計で`20024`ガスが必要です。
ガスは、イーサリアムネットワーク上でコントラクトを実行するために必要な手数料のようなものです。

:::note info
このバイトコードは、**ERC20**や**ERC721**などのスマートコントラクト標準に基づいているかもしれませんが、それ自体で特定の標準に属しているわけではありません。
これは、コントラクトの動作を定義する低レベルのコードの一例です。
:::

# 引用

Alex Beregszaszi (@axic), Nikolai Mushegian <nikolai@nexusdev.us>, "EIP-140: REVERT instruction," Ethereum Improvement Proposals, no. 140, February 2017. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-140.

# 最後に

今回は「コントラクトでエラーが起きた時、実行を中止して原因を返す仕組みを提案している規格である**EIP140**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください！

[Twitter @cardene777](https://twitter.com/cardene777)

他の媒体でも情報発信しているのでぜひ他も見ていってください！

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58


https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4