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

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

Revert opcodeの使い方

EthereumのByzantiumからrevert使うとエラーメッセージをよしなにできるらしいですよ?っていうのが内輪で話題になったので実際にどういう風に利用できるのかを調べてみました。

byzantiumのリリースノートはこちら。
blog.ethereum.org

Revert opcodeの実装状況

EIP140として定義されており、go-ethereumにもvert1.7.2の時点で実装済みでした。
なのですが、Solidityではまだ関数として定義されておらず、assembly codeとしてのみ実装されていました。

solidityのissueにrevert関数を定義しよう的な話も出ているので、将来的には簡単に使えるようになるかもです。
Support reason string in revert (and perhaps require / assert) · Issue #1686 · ethereum/solidity · GitHub

Revert opcodeの役割

いろいろコードを実際に書きながら試した結果

  1. call, callcode, delegatecall時に呼び出し先のcontract( or library)で発生したエラーのメッセージを受け取るためのopcode
  2. revertで終了した場合はガスの消費がない。(ちょっとここはよくわかってない。そこまでの間に実行したコード分は消費される?gethのコード見る限りgasを全部返却してるようなきもするけど。。。)
  3. transactionReceiptやRPCの応答メッセージとしては受け取れない。(あくまでcontractの中でしか扱えない。)

という感じでした。

revert opcodeでメッセージを受け取るサンプル

contractコードの一部を抜粋しています。

function errorfunc() {
 assembly {
      //stringは最初の32byteにlength,次の32byteに文字列が入るので+0x20したものが全体のサイズになる
      revert(msg, add(length, 0x20))
      }
}

// other contract

function callerrorfunc() public {
  string memory resmsg = "";
  message = resmsg;
  if (!lib.call(bytes4(keccak256("errorfunc()")))) {
    assembly {
    returndatacopy(resmsg, 0x0, returndatasize)
    }
    message = resmsg;
  }
}

githubにtruffleプロジェクト全体をpushしてるので必要であればこちらも参照してください。
github.com

まとめ

異常終了時にメッセージを受け取って処理を分岐できるようになるので、コードの幅は広がりそうですね。ただ現状、assemblyで記述しないといけないので敷居は高めです。

現状call(, callcode, delegatecall, create) opcodeの戻り値は1:正常終了、0:異常終了しかないけど、これに2:revertで終了を追加しようかとかって話もあるみたいです。その辺りも含めて整備されていけばSolidityでtry catchをかけるようになるのかもしれないですね。