EthereumでContractを作って運用を考えるとやっぱり頭を悩ませるのが、コードの変更が不可能な事。
基本的にContractであまり複雑な事をすべきではないとは思うけど、それでもやっぱりサービスの成長とともにコードのアップデートも必要となる。
libraryやdelegatecallで外部ライブラリをcallするようにして、参照ライブラリのアドレスを書き換えたりすれば、Contractのプログラムを更新できるなぁとかぼんやり考えていたら、そのものずばりの記事がOpenZeppelinのブログに書いてた。
blog.zeppelin.solutions
今回はこのsolidity-proxyについてまとめた。
上記blogにあるgithubのプロジェクトではtruffle testが動かなかったのと、0.4.18-compilerでwarningが出まくってたのでその辺を修正したものを自分のgithubにあげときました。
#コンパイルエラーについてはgas costのテストをやってたみたいでその時にMergeミスったのかも。試してないけど、1つ2つ前のcommitに戻せばコンパイルは通るようになるのかもしれない。
github.com
構造としてはブログに図があるのでそれをみてもらった方が理解しやすいかと思うけど一応自分なりの説明を箇条書きしておく。
- libraryを利用するとTheContractは実際にはdeploy時にDispatcherとlinkする
- Dispatcherはfallback関数しかもっていない。TheContractからメソッドを呼ばれたらfallback関数を通じて実体のlibraryにdelegatecallする
- DispatcherStorageはlibraryの実態へのadressを持っている。このアドレスを書き換える事でTheContractのdeploy後も実装を入れ替える事ができる。なのでこのパターンの肝はDispatcherとDispacherStorageといっても過言ではない
- LibInterfaceはTheContractの実際の処理を実装するためのInterface(そのままだな。。。)なのでこいつは実際にはいろいろ提供したいサービスに応じて変わる事になる
以下はテスト動かしたりソース読んだりして感じた事とか。あと思いついた改善点とか。
- テスト読んでて思ったのは依存関係というか登場人物が多くて、各libraryとcontractをlinkする時にあれこれどっち参照させたらいいんだっけ?ってなりそうだなぁって感じた。
- Dispatcherは状態を持たないので、本当はこいつもlibraryとして定義したいところだけど、Solidityの言語使用上contractでしかfallback関数を持てないのでcontractで定義してるんだと思う。
- DispatcherStorageのreplaceメソッドは誰でもいじられたら大変なのでContractのcreation ownerのみ実行可能にしとかないといけない。(後で勉強のためにもソース更新しとこ)
- DispatcherStorage#replaceの中でlibraryもcreateされるとセキュリティー上いろいろ安全になるかな?ただlibraryのcreateはできないのでcontractとしてcreateされる。new AnyContract()でcontractから新しいcontract作れるらしいけど試してないのでやてみたい。
最後に、このパターンだと結局TheContractで定義したIFでしかアクセスできないから、新しくTheContractにメソッドを追加したいとかっていう要求には答えられない。そういった要求に答える方法としてはStorageとしてのみのContractとそれを操作するContractとに分けて定義する方が拡張性は高くなりそうと思った。Storageの部分はレイヤーを重ねていけばStorageとしての拡張も可能かな。