💹

Upgradeability - スマコン開発Upgradeabilityの概要

2022/07/27に公開

こんにちは。JPYC研究開発チームのThurendousです。直近ではベアマーケット突入してからマーケットが乱高下しておりますが、アメリカのインフレの上昇により金利がさらに上がると言っていますので、ベアマーケットの正念場はまだこれからだと危惧しているところです。

マーケットの話はさておき、弊社が今年3月にJPYCv1をJPYCv2にアップデートした開発においては、upgradeableなコントラクトにしたいというニーズがあって、そのため当初はUpgradeabilityパターンについて色々と調査していました。せっかくなので記事としてまとめて共有しておきたいと思いました。ご参考になれば幸いです。upgradeabilityについては連載となる予定です。

本文においてはupgradeabilityとはupgradeableなコントラクトの機能と同じ意味で使われたりします。

英語に詳しい方ですとupgradeabilityという英単語について違和感を覚える方もいらっしゃいますが、upgradeabilityあるいはupgradabilityのどちらでも可です。OpenZeppelin公式ではupgradeabilityを多用されているので、本記事もそれに沿った形とします。

前提知識

Upgradeability

Upgradeabilityとは

まず、upgradeabilityとは何かについて説明します。

簡単にいうと改ざん不可能とされるスマートコントラクトを一定レベルまで変更可能にしたということです。

「一定レベル」というワードがついているので、詳細にはとあるアドレスへデプロイしたコントラクトを恣意的に変えられるわけではなく(EVMの属性上、デプロイしたものは変えられません)、実質にユーザーが実行するロジックコードを変更することができるというだけです。

What is a smart contract upgrade?
A smart contract upgrade is an action that can arbitrarily change the code executed in an address while preserving storage and balance.
参照先

場合によりますが上記の通り、「upgradibilityは同じアドレスにおいて任意に実行するコードを変えられると同時に、ストレージやバランスを保てること」と解釈されます。

実際にはいくつかの手法が考えられますが、今回はそれについて後術します。

Upgradeabilityが必要な理由

本来ならば、ブロックチェーン特にイーサリアムにおいては、スマートコントラクトは一旦デプロイすると改ざん不可能という性質を持っているのはご存知のとおりだと思います。設計では改ざん不可能なスマートコントラクトということは強固な信頼性、非中央集権性、安全性を担保したと言えます。改ざん不可能というのは、一見してものすごいメリットのように見えますが、実際には諸刃の剣で開発者側に立ってみればわかると思いますが、大変厄介なデメリットではあります。

従来のweb2.0の開発ですと、一番最初にコードを完璧にできるわけがなく、リリースしてからも徐々にバグなどを修正していくという流れはごくごく普通です。しかし、スマートコントラクト開発になってくると、そうは行かなくなりました。というところで、スマートコントラクトの開発については入念に監査を受けて、様々なチェックを経てから初めて正式なリリースとなり、それでもバグとかが避けられず脆弱性の問題が発生したりします。

そこで考え出されたのはupgradeabilityを持ったコントラクトの開発方式です。万が一バグが発生すると、ユーザーのデータは残したままにしてスマートコントラクトのロジックだけをupgradeするというという手法です。

改ざん不可能とされるスマートコントラクトを変更可能にしたので賛否両論はあります。伝統的な契約の場合、もし両者が後で同意すれば変更可能なのと同じように、スマートコントラクトも変更できるようにしたらどうなのかという発想と同じように思います。もちろん、ある程度の非中央集権性、非改ざん性の妥協はしないとできません。

メリット デメリット
デプロイ後の脆弱性の修正が容易 非改ざん性、安全性、非中央集権性が低下する
新しい機能を追加できる ユーザーは開発者が変な修正をしないと信頼する必要がある
バグ修正が迅速にできるのでセキュリティが向上する 複雑性が上がるため脆弱性・故障のリスクとなる
新しい機能をつけやすかったり、dappを改善しやすくできる upgradeableだからこそ開発者は努力を怠る傾向が現れることもある
スマートコントラクトの中央集権性や安全でないアクセス権の設定により、upgradeが悪用されるリスクがあがる

Ethereumの公式ドキュメントより

上記のように、upgradeabilityは一概によい悪いといえない部分があり、プロジェクトが各自で判断をしなければならいず、ユーザーとしても使うdappsについて、upgradeability機能を使っているかどうかで各自でリスクを判断することになるかと思われます。

実際にTrail of Bitsさんも2018年の記事にて当時のupgradeableパターンについて警鐘を鳴らしたことがあります。スマートコントラクト開発の進歩が早いので、2018年からすでに4年間がたっており(この業界の4年というのは永遠とイコールするくらい長いですが)、色々過去の経験から改善してきてはいますが、いくら改善したとは言え今後もますます進歩していくことは間違いないのがこの業界の特徴でもあると考えます。その発展については注目していく所存です。

Upgradeabilityパターン

Upgradeabilityパターンはいくつか考えられてきました。主要なものの概要について紹介していきます。(Upgradeabilityの歴史をたどると結構色々でてきますが、こちらではそういった細かい内容については割愛しています)

1. Contract migration

コントラクトのマイグレーションという方式は、既存のスマートコントラクトを捨てて新しいスマートコントラクトへ引っ越しをするということを意味します。既存スマートコントラクトのステートとかバランスとかを全部引っ越さなければならず、結構大変な作業になると予想できます。
全く新しいコントラクトをデプロイしているので、いろんな関係者への周知・連絡が必要となってきます。例えば、取引所に上場しているのであれば、古いコントラクトを廃止して新しいコントラクトを適用してほしいと知らせないといけません。また、新しいコントラクトのすべてのステートは更新しなければならず時には非常に高いガス代がかかる場合もあります。

以下はかかるガス代の例です。(2018年時点、ETHの価格が200ドル程度、gasPriceが10Gwei、当時のユーザー数のmigration)

現時点、BNBをとっていうとholder数が5倍になっており、ETHの価格も1400ドル台で、gas priceも10Gweiを超える場合があるので、ガス代の価格が高いことがご理解いただけるかと思います。(gas priceが10Gweiで変わらない前提で、単純に概算して当時の35倍のコストになります。これはユーザーに周知したりするコストがまた別にありますが、こちらが一番面倒かと思います)

時にはやむを得ずこのような手法をとる時もあります。それはコントラクトオーナーのプライベートキーがハッキングされた場合です。

Contract Migration場合、アドレスを変えているので、厳密にはこのパターンは「upgradibilityは同じアドレスにおいて任意に実行するコードを変えられると同時に、ストレージやバランスを保てること」の解釈のupgradeabilityに当てはまりません。

Contract Migrationについて 記事

2. Proxy Patterns

Delegatecall

Proxy Patternついて理解するには、まずdelegatecallについて理解する必要があります。

通常のcallの場合、コントラクトAからコントラクトBへcallする場合、コントラクトBは自分のストレージ領域内でcallについての操作を行おうとします。コントラクトBはこのcallを実行する場合、必要な情報はこのcallのコンテキストから取ります。例えばmsg.senderはコントラクトAとなります。

続いてdelegatecallの場合、実行するコードはコントラクトBのものですが、実行のコンテキストはコントラクトAの元となります。これはコントラクトBのロジックを使ってコントラクトAのストレージを操作できることを意味します。これが実際にSolidityのコントラクトが外部ライブラリを呼び出す仕組みでもあります。

説明

OpenZeppelin blogより引用

Proxy patternはまさにこのdelegatecallを活用しています。データとロジックを分離しています。proxy(データを保存するコントラクト)はimplementation(ロジックを保存するコントラクト)にあるロジックを呼び出してコードを実行しています。

Proxy Patternについて、以下の特徴があります

  • ユーザーはproxyコントラクトとインタラクションをする
  • proxyコントラクトはデータを持っており、ロジックをもっていない
  • proxyコントラクトはimplementationコントラクトのアドレスをもっており、delegatecallを使ってほとんどのcallをimplementationコントラクトへ転送している
  • このパターンによって返された値がユーザーへ返す

このパターンですと、proxyコントラクトはイミュータブルで、implementationコントラクトは変えられます。ここでいうupgradeの意味はimplementationコントラクトを更新して新しいアドレスをproxyコントラクトの中で適用することになります。

このパターンは現在非常に流行っており、パターン1のContract Migrationではできないことができるようになりました。しかし、通常以上の複雑度を伴っているため、使用するにあたり気をつけるポイントがあります。例えば、関数の衝突のようなことが起こります。

主なProxy Patternsとして、Transparent Pattern、UUPS Patternがあります。Transparent PatternはifAdminのmodifierを通して、管理者とユーザーの呼び出し導線を分離して関数の衝突を避けています。UUPS Patternはupgrade関数をロジックコントラクトに配置し、Implementationコントラクトへdelegatecallすることで関数の衝突がなくなっています。

ちなみに、JPYCv2もProxy PatternsのUUPS Patternを採用しています。

3. Diamond Pattern

Diamond Patternは上記のproxyパターンの改善案とみてもよいかもしれません。その違いとしては、Diamond Patternの場合、proxyコントラクトは複数のロジックを含むコントラクトへdelegatecallすることができるようになっています。こうすることでコントラクトを小さいモジュールへ分割できるようになりました。一般的にはブロックチェーンへデプロイできるコントラクトはファイルのサイズ制限がかかりますけれど、小さいモジュールへ分割することでDiamond Patternはこの課題を解決することができました。

ロジックコントラクト群はfacets(Diamondの面)と呼ばれ、proxyコントラクトにおいて異なるfacetにある関数のセレクタを保存するmappingの作成が必要です。ユーザーが関数を呼び出すときに、Proxyコントラクトのmappingをチェックして該当するfacetを取り出して、delegatecallを使ってそれを呼び出して、当該facetのロジックを使うこととなります。

Diamond Patternならではのメリットを列挙します。

  • コントラクトのモジュール化された一部だけをupgradeできるようになる
  • 通常コントラクトのサイズは24kの制限があり、複雑なコントラクトの場合特にこの制限が開発に大きな影響を及ぼします。diamondパターンはこれを解決できる
  • アクセス権については、proxyパターンだと一つのentityがコントラクト全体をupgradeできるのに対し、diamondパターンはモジュールに基づくパミッションを可能にしてentityが一部の関数しかupgradeできないように設定ができる。

筆者が実際に調べた範囲ではdiamondパターンはproxyパターンほど使われておらず、それが理由なのかもしれませんが、OpenZeppelinもメインにProxy Patternのupgradeabilityを採用しています。

Diamond PatternはまだProxy Patternほど使われていないし、十分にbattle test(市場での実用によるテスト)されてないと思われますので、採用する際には慎重に検討されたほうが良いかと考えています。

採用したプロジェクト

ERC20トークンですと、ごく普通にupgradeabilityが採用されていると考えています。特にUSDCやJPYCのような中央集権的なプロジェクトはupgradeabilityの採用が一番しっくりきます。

USDCのupgradeability 記事
JPYCv2のupgradeability GITHUB

また、NFTプロジェクトですと、The Saudisというプロジェクトはdiamondパターンを採用しているそうです。本当は一つのwalletだと一個のNFTしかmintできないのですが、匿名ユーザーRightblockがそのルールを迂回できて大量にThe SaudisのNFTを入手して市場にて売りつけました。プロジェクト側はその後コントラクトをupgradeして大量に売りつけたNFTを取り戻したそうです。

まとめ

以上はupgradebilityの概要となります。色々と発展の歴史もありますが、ここでは現在の最新の情報についてご紹介しました。

全体像を少しでも把握していただくためにお役に立てれば幸いです。正直upgradeabilityについてはブロックチェーンの基本的な改ざん耐性や非中央集権制と矛盾している(ガバナンス機能をつけることで非中央集権性を損なわない方法もありますが)ので、なおさら理解しづらいと思います。そういった概念的な部分をクリアできれば、コードもすっと頭に入るのではと考えています。

これからもブロックチェーン等の開発に役に立つ技術的な知見を記事にしていきますのでよろしくお願いします!

日本初のブロックチェーン技術(ERC20)を活用した日本円ステーブルコインJPYCはこちらから購入できます!
JPYC社はブロックチェーンエンジニアを募集中です!こちらからご応募お願いします!(タイミングにより募集を行なっていない場合があります)
また、ラボ形式でブロックチェーンに関する講義をしているJPYC開発コミュニティにも是非ご参加ください!

Discussion