🐙

Ceramic・IDXを使ったPlatformlessなアプリケーション開発

2021/04/05に公開

分散化・脱PF化の流れ

大きく世の中の流れを捉えると、様々な分野や粒度での分散化・脱中央化というのは今の主流になっていると思う。

わかりやすい例で言えば、無料で視聴できる動画コンテンツは昔は大きな力をもったテレビ局でしか流すことができなかったが、今ではYoutubeなどを利用することによって個人でも配信をすることができ、それがテレビを覆すほどの需要を得ている。

ただし、Youtubeの例だとTV → 個人の流れでコンテンツクリエイターの分散化に成功したが、Youtubeという巨大PFを考えれば結局のところ分散化されてはいない。「エンタメコンテンツ業界で何が起こっているのか(マクロ編)」にもあるが、巨大PF化で手数料を取ることにより、収益を上げている人がコンテンツクリエイター → PF側へと移りパワーバランスがPF寄りになってしまっている。

こういった流れを断ち切ろうとしている分野として、Decentralized finance(通称DeFi)という分散型の金融があげられる。これは既存の金融は政府や銀行・証券会社などの中央的機関を必要としたものであり、自分たちのとても大切な資産をそれらに依存させる(カウンターパーティーリスクや不透明性など)のは危険と考え、こういった中央機関が不要な金融をブロックチェーンなどの分散技術を利用して作ろうというものである。(詳しくは「10分で分かるDeFiの仕組み──知っておくべき8つのキーワード」)

そして、アプリケーションの開発にもこの分散化の流れがあるので、これについて紹介したいと思う。

アプリケーションの分散化とは?

ブロックチェーンやWeb3.0の流れもあってか、アプリケーションの分散化というものも最近よく見られる動きである。DApps(Decentrilzed Application)と呼ばれる、ブロックチェーンや分散型のネットワークに載せたアプリケーションを開発することによって、管理者なしで動くアプリケーションを実現することができたりする。

これらの大きな特徴としては、IDプラットフォームが存在しないこととクラウドなどの中央的なPFにホスティングされていないことだと考えている。IDプラットフォームはユーザーが自分で管理し、ユーザー自身であることを証明できる鍵をキーウォレットで扱えることによって、ディスラプトすることができ、クラウドはブロックチェーンや分散型のネットワークによって代替できると考えられている。

また、ウォレットだけだとIdentity Layerを代替するには不十分であり、ここにはSSI・DIDという個人でIDを管理できるしくみを導入することによって、IDプラットフォームなどの管理主体を排除することができる。(詳しくはSSIとDIDで何を解決したいのか?(β版)アイデンティティは誰のもの? Hyperledger Indy & AriesでSSI

そして、この分散化の流れを感じるためにも、今回はDIDで認証を行い、分散型のネットワークに自分のデータをWrite・Readできるようなアプリケーションの開発を実現するCeramic・IDXというものを紹介と実装を説明したい

https://raw.githubusercontent.com/winor30/ceramic-idx-application-example/main/resources/overview-plattformless.jpg

Ceramic・IDX

Ceramicとは今の大手PFが管理しているデータレイヤーを分散化しようとする3BoxLabが開発しているプロジェクトである。彼らはこの問題をDIDや分散型ストレージであるIPFS・独自開発している分散ネットワーク等を利用して、分散的で改ざんが不可能なデータインフラを提供している(まだα版)。ざっくりいうと、ドキュメント型のDBで、データの更新や読み取り権限によってはDID所有者でないと行えないようなものである。

そして、IDXはDIDをベースに様々なデータを紐付け、アプリケーション横断でのデータの共有などを実現するためのプロトコルとなっている。IDXは基本的にCeramicを開発している3BoxLabが同じく開発しており、IDXのデータストレージとして、Ceramicがメインで利用されるが、他の分散型のデータストレージ(EthereumコントラクトやIPFS、Filecoinなど)も利用することができるようにモジューラブルな設計となっている。

これらにより、自分のデータは自分で管理ができ、そのデータを様々な異なるアプリケーションで提供する(相互運用)ことができるオープンなインターネットを実現できる。

実装

今回の対象

今回はCeramicとIDXを利用して、Metamaskと呼ばれるEthereumのウォレットで管理するDIDにユーザーの名前と自己紹介を紐付けるWebアプリケーションを実装した。また、アプリケーション自体は1つだが、この名前と自己紹介はこのDIDに紐付いているため、他のアプリケーションでも利用することができる。

WebアプリはNext.js x Tailwind x TypescriptでVercelでホスティングしている(ここも分散的なホスティング手段を考えたが、色々盛り込むとブレるので、分かりやすいVercelを利用した)

https://raw.githubusercontent.com/winor30/ceramic-idx-application-example/main/resources/example-app-overview.jpg

実装内容

コードの詳細は下記リポジトリから確認できる。

https://github.com/winor30/ceramic-idx-application-example

以下では、主要な部分に関して説明していく

Ceramic・IDXライブラリ

src/libs/idx/index.ts から確認できるように、CeramicとIDXのインスタンスをライブラリを使ってDIDに紐づくデータの管理ができる。

Ceramicとhttpで通信するためのインスタンス(@ceramicnetwork/http-client)をIDXインスタンスに渡すことによって、CeramicをIDXのバックエンドとすることができる。

また、CERAMIC_CLAY_URLは現在テストネットのurlを指定している。

import Ceramic from '@ceramicnetwork/http-client';
import { IDX } from '@ceramicstudio/idx';
import { CERAMIC_CLAY_URL } from './constant';

let ceramic: Ceramic;
const getCeramic = () => {
  if (ceramic) return ceramic;
  ceramic = new Ceramic(CERAMIC_CLAY_URL);
  return ceramic;
};

let idx: IDX;
const getIdx = () => {
  if (idx) return idx;
  const ceramic = getCeramic();
  idx = new IDX({ ceramic });
  return idx;
};

export { getIdx, getCeramic };

DIDによるCeramic上での認証

DIDを所有していることを証明するためには、Ceramicインスタンスにその情報を渡す必要があり、下記のコードで実現できる。

この大まかな流れは

  1. Ethereumのウォレット(Metamask等)にブラウザからアクセスできるようにする
  2. ウォレットのProviderインスタンスを取得し、それをラップしてauthProviderを作る
  3. 3id-connectでauthProviderからDIDを所有していることが証明できるdidProviderを生成する
  4. ceramicインスタンスにdidProviderをセットする(ceramicインスタンスがdidの機能を使えるようになる)
export const getBasicProfile = async (): Promise<Error | LoginData | null> => {
	const addresses: string[] | Error = await window.ethereum.enable().catch(returnErr);
  if (addresses instanceof Error) {
    return addresses;
  }

  const address = addresses[0];
  if (!address) {
    return new Error('address is undefined');
  }

  const authProvider = new EthereumAuthProvider(window.ethereum, address);
  const threeIdConnect = getThreeIdConnect();
  const connected = await threeIdConnect.connect(authProvider).catch(returnErr);
  if (connected instanceof Error) {
    return connected;
  }

  const idx = getIdx();
  const didProvider = threeIdConnect.getDidProvider();
  const result = await idx.ceramic.setDIDProvider(didProvider).catch(returnErr);
  if (result instanceof Error) {
    return result;
  }
...
};

イーサリアムウォレットからブラウザにアクセスできるようになる処理は、MetamaskやTrustなどの標準的なブラウザの場合はwindow.ethereum.enable() で基本的にはブラウザで利用する権限を付与できる。(ブラウザの挙動としては、ウォレットが立ち上がり、許可しますか?とユーザーに問いかける)

そして、EthereumAuthProviderでラップしている部分は3id-connectというライブラリの仕様に合わせて、Ceramicでも利用できるようにするためである。3id-connectは3ID DIDという種類のDID(didメソッドがdid:3:***)をブロックチェーンウォレット(現状だとEthereumとEOS)で扱えるようにするためのライブラリとなる。ここで重要な考え方としては、DIDは基本的にはW3Cで規格化されている署名ができ、その署名を検証できればどういう管理をしても良い。そして、3id-connectではその管理できるものをブロックチェーンウォレットとして利用しようという考え方であり、必ずしもブロックチェーンウォレットでないとだめというわけではない。

threeIdConnect.connectでこのブロックチェーンウォレットが正しくDIDを管理できるか?とユーザーにDIDを管理しても良いかという問い合わせを行い、これに成功するとthreeIdConnectでdidの所有を証明できるdidProviderを利用することができるようになる。

最終的にはこのdidProviderをceramicインスタンスにセットすることで、ceramicインスタンスでもDIDの証明を利用することができる。

プロフィール情報の取得

プロフィール情報の取得に関しては基本的に特にDIDの認証は必要ない。

ここは少しリサーチ不足なのだが、基本的に現状のIDXの仕様的にget(DIDに紐づくデータの取得)は認証なしでできそうである。ただし、将来的には認証が必要なPrivateだったり特定の権限が必要な情報の取得も増えるのかなと思う。

コード的にはとてもシンプルで、idx.get(alias, did)でデータを取得できる。このエイリアスに関しては、事前にスキーマが決まっており、そのスキーマを指定するためのエイリアス名となっている。また、このスキーマは追加することもできる。そしてDIDに対してスキーマで定義できるデータは基本的に1つとなる(例:basicProfileスキーマのデータは1つのDIDで1つしか存在しない。)

export const getBasicProfile = async (): Promise<Error | LoginData | null> => {
...
	const did = didProvider.accountId;
  const basicProfile = await idx
    .get<LoginData | undefined>(constants.definitions.basicProfile, did)
    .catch(returnErr);
  if (!basicProfile) {
    return null;
  }
  return basicProfile;
};

プロフィール情報の更新

プロフィール情報の更新に関しては、DIDの認証が必要である。

当たり前だが、だれでも自分に紐づく情報を変更できてしまうのを防ぐためである。

これもエイリアスを利用して、そのエイリアスのスキーマに沿ったオブジェクトをsetすることで更新できる。

export const useSignUp = () => {
...
  const signUp = async (data: { name: string; description: string }) => {
...
    const basicProfile = await getBasicProfile().catch(returnErr);
...
    // DIDProviderをset済みのidxインスタンス
    const idx = getIdx();
    const docID = await idx
      .set(constants.definitions.basicProfile, {
        name: data.name,
        description: data.description,
      })
      .catch(returnErr);
...
  };
...
};

動作結果

vercelにデプロイしたデモアプリはこちら

https://ceramic-idx-application-example.vercel.app

(注意:↓のウォレットは今回新しく生成したテスト用のアカウントである。)

https://raw.githubusercontent.com/winor30/ceramic-idx-application-example/main/resources/cermaic-app-screenshots.jpg

まとめ・考察

今回は分散化・脱PF化背景を説明しつつ、それが実際に現在進行系で行われているプロジェクトであるアプリケーション開発の分散化を目指したCeramicとIDXについて紹介とこれらを使ったアプリケーションを実際に作ってみた。

やってみて思ったこととしては、思ったより今の開発体験だったりを損なわずに使いやすい方法で作られているなと感じた。ただし、UX面に関してはウォレットや署名部分だったりでかなり複雑にユーザーは感じるので、改善が期待される。(ここに関してはportisAuthereumなどのwebアプリに組み込めるウォレットを利用すれば多少は和らぐ)

また、分散型ネットワークの特徴として、代替のケースで中央型よりもパフォーマンスは悪くなりやすいので、ここもUXやシステムの構築難易度が上がってしまうところでもあると思う。
IDXのデータストレージを柔軟に選べることと似ているが、分散的なシステムにも中央的なシステムどちらにもメリットはあり、これらをプロダクトを実現のための1つの要件として取捨選択し、システム・アプリケーションの開発をしていくのがエンジニアとして考えなくてはいけなくなる部分なのかなと思った。

参考

Discussion