Solidityのエラー処理
執筆者 Shray Jain
Solidityにおけるエラーハンドリングはどのように機能するか
Solidityはエラー処理にstate-revertingな例外を使用しており、この例外は現在の呼び出しでstateに加えられたすべての変更を取り消すと同時に、呼び出し元にエラーを通知する。
Solidityは、Ethereumなどのブロックチェーン上でスマートコントラクトを実装するためのオブジェクト指向プログラミング言語であり、コンパイル時またはランタイムに発生しうる問題に対処するための多数の関数を備えている。構文エラーのチェックはコンパイル時に行われるが、ランタイムエラーは検知が難しく、主にコントラクトの実行過程で発生する。ランタイムエラーの例としては、ゼロ除算エラーや配列のインデックス範囲外エラーなどが挙げられる。
実際のところ、Solidityにおけるエラーハンドリングはatomicity(原子性)という特性を保証するものである。スマートコントラクトの呼び出しがエラーで終了すると、state変更(変数や残高などへの変更)はすべて、コントラクト呼び出しの連鎖を遡ってすべて取り消される。
開発者はインターフェースを宣言することで、他のコントラクトと直接やり取りできる点に注意する必要がある。Ethereumブロックチェーン上では、トランザクションはatomicである。つまり、トランザクションは完全に完了するか、stateに影響を与えずに完全に取り消されるかのいずれかである。
Solidityのエラーハンドリング関数は主に3つある
Solidityにおけるエラーハンドリングは、原則として特別な関数であるassert、require、revertの3つによって管理される。バージョン0.4.10までは、Solidityにはthrow文が1つ用意されているのみだった。
SolidityはEthereum Virtual Machine(EVM)をターゲットに設計されており、C++、Javascript、Pythonの影響を受けている。Solidityを使用することで、開発者は投票、クラウドファンディング、マルチシグウォレット、さらにはブラインドオークションといった用途のコントラクトを作成できる。
実際には、これは開発者が値をチェックしエラーを発生させるために複数のテスト関数を書く必要があったことを意味しており、これはガスに関して最適化されていなかった。Solidityバージョン0.4.10のリリースにおいて、新たなエラーハンドリング構文であるassert、require、revertが導入され、throwは非推奨となった。
require関数とは何か
_require_関数は、実行前に入力や条件を検証するために使用される。例えば、条件がfalseの場合、require関数は即座に実行を停止する。言い換えれば、requireはゲートチェック修飾子として機能し、条件を満たさないロジックがそれ以上関数の実行にアクセスすることを防ぎ、その結果エラーを発生させる。requireは、ロジックフローのゲーティングや、関数へのユーザー入力の検証に適している。
require文は、コード実行前に満たされるべき関数実行の前提条件を宣言するものである。_require_関数は単一の引数を受け取り、評価後、trueまたはfalseのブール値を返す。false条件によって実行が終了した場合、未使用のガスは呼び出し元に返却され、stateは元の状態に戻される。カスタムの文字列メッセージを追加することもできる。
以下はSolidityにおけるrequire文の例である:

require文のユースケース
実用上の理由から、開発者は以下のようなシナリオで_require_を使用することができる:
- 外部コントラクトからのレスポンスの検証
- 最終実行前のstate条件の検証
- ユーザー入力の認証
revert文とは何か
revertはいかなる条件も評価せず、いかなるstateや文にも依存しない。revert文はrequire文と類似しており、revert関数はrequire関数と同じ種類のエラーを扱うことができるが、より複雑なロジックゲートに適している。
_revert_文が呼び出されると、未使用のガスが返却され、stateは元の状態に戻る。カスタムメッセージを追加できる点は_require_関数と同様である。
以下はSolidityにおけるrevert文の例である:
assert関数とは何か
assertは、決してfalseになるべきではないコードをチェックするために使用される関数であり、あり得ないシナリオを防ぐ上で重要な役割を果たす。assert関数がtrueのブール値を返した場合、致命的なバグが表示され、プログラムは実行されない。
_require_関数や_revert_関数とは対照的に、_assert_は未使用のガスを一切返却せず、代わりに_assert_関数はガス供給を消費した上でプログラムを元の状態に戻す。興味深いことに、Byzantiumフォーク以前は、_require_関数と_assert_関数はどちらも同一の挙動を示していたが、異なるopcodeにコンパイルされていた。
assert型の例外
- 値がゼロで剰余演算またはゼロ除算される場合
- 関数のゼロ初期化された変数が呼び出される場合
- 負の値または大きすぎる値がenumに変換される場合
- 想定範囲を超える負のインデックス、または想定より大きいインデックスで配列にアクセスする場合
_assert_文の例:
assert文のユースケース
理論上、_assert_は_require_関数と比較して使用頻度が低いはずである。開発者は、以下のようなユースケースにおいて_assert_関数の使用を検討すべきである:
- 変更後のコントラクトstateの検証
- 決して起こり得ない条件の回避
- オーバーフローおよびアンダーフローパラメータのチェック
- 不変条件(invariants)の検証
require対revert対assert
以下は、Solidityのエラーハンドリング関数である_require_、revert、_assert_の3つすべてについての簡潔な要約である。
require
- 関数の冒頭で使用される
- 不正な入力に対して検証を行う
- 実行前のstate条件を検証する
- 余ったガスを返却する
revert
- requireと同一
- より複雑なロジックフローゲート(複雑なif-thenブロックなど)に有用
- 余ったガスを返却する
assert
- 関数の末尾で使用される
- あり得ないことを検証する
- 静的コード解析ツールにとって重要
- 余ったガスを返却しない
requireとSolidityのエラーハンドリングについてさらに学ぶには
Solidityのエラーハンドリングについてさらに学びたい開発者は、Alchemy Universityの無料Solidity開発者クラッシュコースへの登録を検討するとよい。この無料の7週間のブートキャンプは、web2開発者や全くの初心者がSolidityスマートコントラクトの書き方を学ぶのに役立つ。開発全般に不慣れな開発者であれば、Ethereumブートキャンプを始める前の前提知識として、Alchemy Universityの3週間のJavaScriptクラッシュコースを受講するのがよいだろう。
関連する概要

ブロックチェーンで魔法を生み出す
Alchemyは、最も強力なweb3開発者向けプロダクトとツールを、豊富なリソース、コミュニティ、そして卓越したサポートと組み合わせて提供します。


