Flow ブロックチェーン|Cadence 言語の紹介

Ara
Flow Japan

--

こんにちは。エンジニアの荒川です。初回の記事で後半パートを書かせていただきました。今回の記事では、Flow 公式サイトの “Introduction to Cadence” を日本語に翻訳したものをお届けします。この記事を読むと、Flow において重要な柱である Cadence 言語の特徴と必要性がよくわかります。Flow のドキュメントは非常に読み応えがあっておもしろいので、ぜひ読んでみてください。最後に、私の感想も少し書きました。

目次

  • 新しいプログラミング言語
  • Cadence プログラミング言語の特徴
  • 既存の言語における課題への対処
  • リソースによる直感的な所有権
  • Cadence をはじめよう

私たちは通常、ユーザーアカウントに保存されているプログラムをスマートコントラクトと呼んでいます。スマートコントラクトとは、信頼できる第三者を必要とせずに契約を検証・実行するプログラムのことです。ブロックチェーン上で実行されるプログラムは、銀行などの中央機関に頼ることなく重要な機能(通貨など)を取り持つため、一般にスマートコントラクトと呼ばれています。

新しいプログラミング言語

Cadence(ケイデンス)はリソース指向のプログラミング言語で、スマートコントラクトのプログラミングに新しい機能を導入し、開発者がコードの安全性、明確性、親しみやすさを確保できるようにします。これらの機能の一部を紹介します。

  • 型安全および強力な静的型システム
  • リソース指向プログラミング
    線形型とオブジェクトを組み合わせた新しい概念。リソース(とその関連アセット)が一度に一箇所にしか存在できず、コピーできず、誤って紛失・削除されないことを保証することで、デジタル所有権の安全で宣言的な(訳注:いちいち細かく指定しなくてよい)モデルを作れる。
  • 関数とトランザクションの事前条件・事後条件
  • オブジェクトへのアクセスは、所有者とそのオブジェクトへの参照を持つ者のみに制限される(訳注:最小権限の原則 を参照)

Cadence の構文は、Swift と Rust にインスピレーションを得ています。また、リソース型は、Libra チームが開発しているプログラミング言語である Move とよく似ています。

Cadence プログラミング言語の特徴

新しい高級プログラミング言語 Cadence は、次の要件を遵守します。

安全性とセキュリティ

安全性とは、スマートコントラクトの基本的な信頼性(バグがなく、機能を果たすこと)です。セキュリティとは、ネットワークやスマートコントラクトに対する攻撃(悪意のある不正行為)を防ぐことです。スマートコントラクトでは、ブロックチェーンの不変性により、価値の高い資産を扱うことが多いため、安全性とセキュリティが非常に重要になります。コードの監査とレビューは、スマートコントラクトの開発において重要な要素となりますが、Cadence は、最高レベルの安全性とセキュリティを基盤として維持して効率を最大限に高めます。これは、強力な静的型システム、契約による設計、線形型に発想を得た所有権のプリミティブ型によって実現されています(詳細は後述)。

明確さ

コードは読みやすく、その意味をできるだけ明確にしなければなりません。また、検証に適したものでなければならず、ツールが安全性とセキュリティを保証できるようにしなければなりません。これらは、コードを宣言的な(訳注:いちいち細かく指定しなくてよい)ものにして、開発者が自分の意図を直接表現できるようにすることで実現できます。このような意図を設計で明示することで、可読性とともに監査やレビューの効率化が図られ、冗長性のコストを抑えられるようになります。

親しみやすさ

コードの記述とプログラムの作成は、可能な限り親しみやすいものにする必要があります。Swift や Rust などの言語の機能を組み込んでいるため、開発者は Cadence の構文や意味に精通しているはずです。実用的なツール、ドキュメント、いくつかのコードの例によって、開発者はプログラムの作成を迅速かつ効果的に開始できます。

開発者体験(DX)

開発者は、アプリケーションのロジックからチェーン上のバグ修正まで、開発のライフサイクル全体を通じてサポートされる必要があります。

リソースによる直感的な所有権

リソースは、構造体に似た複合データ型で、アセットの直接的な所有権を表現します。Cadence の強力な静的型システムは、リソースが一度に 1 つの場所にしか存在しないことを保証し、コーディングミスによるコピーや紛失を防ぎます。現在、ほとんどのスマートコントラクト言語では、所有権を記録するために台帳スタイルのアプローチを使用しています。Cadence のリソースは、アカウントのストレージにリソースを保存することで、アセットの所有権をアカウントに直接結びつけます。その結果、所有権はスマートコントラクトのストレージに集中することはありません。各アカウントはそのアセットを所有しており、中央のスマートコントラクトの仲介を必要とせずに、アカウント間で自由にアセットを転送できます。

既存の言語における課題への対処

他の言語は、スマートコントラクトの開発を開拓してきましたが、次世代アプリケーションの長期的な実行可能性を考えると十分ではありません。

安全性

安全性とは、スマートコントラクトが意図したとおりに機能を実行するための信頼性です。これは、スマートコントラクトのデプロイ後の変更不可能な性質に大きく影響されます。開発者は、スマートコントラクトをリリースする前に、潜在的・壊滅的なバグの混入を回避する必要があります。たとえば、2016年に、DAO のコードの見過ごされていた脆弱性により、スマートコントラクトから数百万ドルが吸い上げられ、最終的に Ethereum は 2 つの別々のブロックチェーン(Ethereum と Ethereum Classic)に分岐しました。

バグ修正は、スマートコントラクトが変更をサポートするように設計されている場合にのみ可能です。しかしこの機能は、複雑さとセキュリティの問題も引き起こします。監査・レビューによって、バグのないスマートコントラクトを保証できますが、スマートコントラクトのコアロジックを正しく機能させるというすでに多くの時間がかかるタスクに加えて、さらに時間がかかります。

見落とされたミスは、最も有害なシナリオを引き起こします。既存の言語では、不変の量をチェックせず、それらを表現するのも難しいため、通貨の金額やアセットは簡単に紛失・複製できます。例えば、送金額を示すただの数値は、誤って(または悪意を持って)計算されたり、無視されたりする可能性があります。

言語によっては、開発者が忘れそうな振る舞いをします。例えば、固定の範囲を持つ型は、オーバーフローやアンダーフローを考慮せずに金額を表現します。Solidity では、オーバーフローが発生すると値がひと回りしてしまいます。また、Solidity では、初期化せずに変数を宣言できます。開発者が初期化を忘れて、コードの別の場所で特定の値を期待して変数を読み込もうとすると、問題が発生します。

Cadence は型安全であり、強力な静的型システムを持っています。これにより、コンパイル時に(つまり、プログラムがオンチェーンで実行される前に)、重要なクラスの誤った挙動・望ましくない挙動を防げます。型は静的にチェックされ、暗黙的に変換されません。また、Cadence は、アンダーフロー・オーバーフローを防止したり、NULL を明示するオプションを導入したり、変数の初期化を常に要求したりするなど、プログラムの安全性を向上させています。これにより、スマートコントラクトの動作が明らかで、コンテキストに依存しないことを保証します。

セキュリティ

セキュリティと安全性を組み合わせることで、許可されていないアクセスを防ぎ、プロトコルで許可されたアクションのみを実行できるようにすることで、スマートコントラクトを長期にわたって正常に実行できます。一部の言語では、関数はデフォルトで公開されており、悪意のあるユーザーが攻撃ベクトルを見つけられる脆弱性を生み出します。Cadence は Capability-based security を用いています。これにより、型システムは、ユーザーと開発者が制御できるルールに基づいてアクセスを制御できます。

セキュリティは、他のスマートコントラクトとやり取りする際に考慮すべき事項です。外部からの呼び出しにより、悪意のあるコードが実行される可能性があります。たとえば Solidity では、呼び出された関数シグネチャが存在しない場合、fallback 関数が実行されます。この機能は悪意を持って使われる可能性があります。多重継承やオーバーロード、ディスパッチなどの言語の機能も、呼び出されるコードの判別を難しくします。

Cadence では、プログラムの安全性とセキュリティは、契約による設計(訳注:コードの中にそのプラグラムが満たすべき仕様を記述する技法)と 所有権のプリミティブ(訳注:所有権を表す専用の型を用意していること)によって強化されています。契約による設計では、関数やインタフェースの事前条件・事後条件を宣言的に記述することで、呼び出し元が呼び出されたコードの動作を確実に把握できるようにします。所有権のプリミティブは線形型(訳注:Rust 言語などで使われている、プログラム内で宣言した変数は必ず消費されなければならないという考え方)にヒントを得ており、重要な資産を扱う際の安全性を高めます。これにより、例えば貴重な資産が誤って、あるいは悪意を持って紛失したり、複製されたりしないようにします。

明快さと親しみやすさ

暗黙性、コンテキスト依存性、表現性は、開発者がしばしば遭遇する言語の課題です。これらの課題は、言語およびそれを使ったプログラムの明快さ(読みやすいか、意図を判断しやすいか)と親しみやすさ(解釈しやすいか、書きやすいか)に影響を与えます。たとえば Solidity では、ストレージは Key-Value を使った低レベルの方法で実装する必要があり、開発者の意図を汲むことを難しくしています。別の例として、混乱を招く構文があります。=+ は正しい構文であり、意図したインクリメントではなく代入になります。また、Solidity は、意図しない結果を引き起こしうる、一般的ではない挙動を持っています。多重継承はプログラムの予期しない動作を引き起こす可能性があります。コードをテスト・監査しても、この問題を特定できる可能性は低いです。

Ethereum のコード不変性は、拡張や限定的な修正を可能にすることについて、考慮の必要性を示しています。Trail of Bits(訳注:アメリカのセキュリティ監査会社)は、Ethereum の現在のアップグレード戦略とその問題点について詳しく説明しています。アップグレード性に対して「データ分離」アプローチを用いる開発者は、データ構造の複雑さの問題に直面する可能性があります。「delegate call を使ったプロキシ」を用いる開発者は、メモリレイアウトの一貫性の問題に直面する可能性があります。いずれにしても、これらの課題は、アプローチのしやすさと全体的な拡張性を損なうことになります。

Cadence は、拡張性、コードの再利用、コントラクト間の相互運用性を可能にするインターフェースを使って、プログラムの明快さと拡張性を向上させます。また、Cadence のモジュールには、構成可能で透明性のあるアップグレード機能が組み込まれており、プロジェクトがコードを不変にする前のテストと反復を可能にします。

Cadence では、関数の引数の意味を記述するために引数ラベルを使用することができます。また、便利なデータ構造(Dictionary、Set など)や固定小数点演算のような一般的なユースケースに対応したデータ型を持つ豊富な標準ライブラリを提供しており、通貨を扱う際に役立ちます。

リソースによる直感的な所有権

現在、スマートコントラクトのほとんどの言語は、所有権を記録するために台帳スタイルのアプローチを使用しています。このアプローチでは、アセットは中央台帳のエントリーとしてスマートコントラクトに保存されます。この台帳はアセットの所有権に関する情報源となります。この設計には、特にあるアカウントに紐づく複数のアセットの所有権を追跡する場合に、欠点があります。アカウントが所有するすべてのアセットを見つけるには、このアカウントが利用した可能性があるすべてのスマートコントラクトを列挙し、アカウントがこれらのアセットを所有しているかどうかを検索する必要があります。

Cadence のようなリソース指向の言語では、リソースをアカウントのストレージに保存することで、アセットを、所有するアカウントに直接結び付けます。その結果、所有権はひとつのスマートコントラクトのストレージに一元化されません。代わりに、各アカウントは独自のアセットを所有・保存し、中央のスマートコントラクトを介さずに、アセットをアカウント間で自由に送付できます。

リソースは線形型に影響を受けており、高い価値を持つアセットを操作する際の安全性を高めます。Cadence の型システムによって実現されるリソースは、アセットが正しく操作され、悪用されないことを保証します。

  • すべてのリソースには、1 人の所有者がいる
    リソースが関数のパラメータや変数の初期値などに使われている場合、オブジェクトはコピーされません。代わりに新しい場所に移動され、古い場所では直ちに無効になります。
  • リソースの所有権が正しく転送されなかったり、プログラムがリソースに複数の所有者をセットしようとしたり、リソースが所有者を持たない状態になった場合などに、言語はエラーを報告する
    例えば、リソースは正確に 1 つの変数にしか代入できず、関数に複数回渡すことはできません。
  • リソースをスコープ外に出すことはできない
    関数やトランザクションがアカウントのストレージからリソースを削除する場合、アカウントのストレージ内でトランザクションを終了するか、明示的かつ安全に削除する必要があります。リソースには「ガベージコレクション」はありません。

リソースオブジェクトの特別なステータスは、ランタイムによって強制されています。もし、リソースオブジェクトが単なるコンパイラの抽象化であれば、悪意のあるコードが値の保証を破ることは容易です。

リソースは、プログラミング環境でのアセットの使用方法を、現実世界のアセットに似たものにします。ユーザーは自分の通貨やアセットを自分のアカウントやウォレットストレージに保存し、好きなように使うことができます。ユーザーは、リソースの独自のロジックや構造を定義でき、保存方法に柔軟性を持たせられます。さらに、誰もが自分のアセットを保管しているため、ステート利用料の計算・課金は、ネットワーク内のすべてのユーザーに渡り公平でバランスのとれたものになります。

インタープリター型言語

現在、Cadence は、コンパイル型言語ではなく、インタープリター型言語です。これは、Cadence のアセンブリ、バイトコード、コンパイラ、または CadenceVM がないことを意味します。

言語の構造はコンパイル(静的型付けなど)に適していますが、最初のバージョンにインタープリターを使用すると、言語機能を定義するときに、言語機能をより迅速に改良できます。

Cadence をはじめよう

Cadence と Flow の目標・設計について学習したので、Flow エミュレータとツールを使い始める準備ができました。First Steps のページに移動して、チュートリアルを開始しましょう。

蛇足になってしまうかもしれませんが、少し感想を。
Cadence は、学術的な研究からスタートしていることもあり、コンピュータサイエンスをかじった人なら聞いたことのあるキーワードがいろいろとでてきます。これが、多くのエンジニアを惹きつける理由のひとつになっていると思います。正直な感想として、Cadence は難しいと思います(少なくとも私にとっては)。これは、スマートコントラクトをつくるときの考え方を変えなければならないからです。当然、既存の Solidity などのコードは Flow にそのまま移行できません。既存の資産を断ち切ってまで、新しい言語を採用した理由は、現状のブロックチェーンプログラミングの問題を根本的に解決するためです。これは非常に共感できることで、この精神は見習わなければならないなと感じます。

さて、この説明を読んだだけでは、まだイメージがわかないことも多いと思います。実際にコードをみて、手を動かすと、ここに書かれていることがよく理解できます。ただいま、Cadence のチュートリアルを日本語に絶賛翻訳中なので、今後の記事にもぜひご期待ください。

次の記事:「Cadence チュートリアル(1)最初のステップ

--

--

Ara
Flow Japan

ソフトウェアエンジニア。生物学、民俗学、仏教、神道、メディアアート、博物学、フォント、ブロックチェーンなどに興味あり。