Gaudiy Tech Blog

Gaudiyの技術、開発組織、カルチャーについてお伝えするブログです

Server-Driven UIの採用背景と実装について

こんにちは。ファンと共に時代を進める、Web3スタートアップのGaudiyで、フロントエンドエンジニアをしているkodai(@r34b26)です。

Gaudiyでは、Airbnbが採用していることで有名な「SDUI(Server-Driven UI)」という設計手法を取り入れています。

先月のTech Blogでは、ユーザーに対してファンダムな体験を届けるために実践している、スキーマ駆動開発についてお伝えしました。

techblog.gaudiy.com

今回は少し視点を変えて、顧客やユーザーと対峙する社内メンバーに対して、ファンダムな体験を届けるために実践している、SDUIについてまとめてみます。

GaudiyでSDUIを取り入れた理由や、その実装方法なども書いてみたので、一事例としてよければご参考ください。

1. SDUI(Server-Driven UI)とは

SDUIはおそらく、Airbnbが使用する設計手法として、その名称とともに広がったのが始まりです。

medium.com

UIの表現方法をすべてサーバーサイドで決定し、クライアントに返す、という手法になります。

通常のアプリケーション開発では、レンダリングロジック、ルーティングなどをクライアント側で決めますが、SDUIの手法ではこれらをサーバー側で決定しています。

日本語だとこちらのnoteがわかりやすいです。

note.com

2. SDUIのメリット

先述したAirbnbの記事や、一般的に説明されているメリットとしては、以下になります。

  1. UIの変更にアプリケーションのデプロイを必要としない

    サーバー側のロジックや設定を変更することで、クライアント側の表示を変更できるため、クライアントアプリのデプロイが必要ありません。そのため、デプロイまでの社内プロセスや実行時間を待つことなく、UIに対する変更が可能です。

  2. アプリケーションの審査を必要としない

    とくにネイティブアプリでのメリットになりますが、UI更新にデプロイを必要としないため、Apple, Googleの審査を毎回待つことなく細かい変更や巻き戻しができます。これにより、社内でコントロール不可能なタイムロスを圧縮することが可能です。

  3. 複雑な表示切り替えに拡張可能

    レコメンドエンジンを用いたユーザーごとの表示切り替えによる、行動促進や広告配信ができます。また、ABテストや限定リリースなど、リッチなロジックによる制御を組みやすくなります。

つまり、クライアントの都合による変更反映までのタイムラグをできる限り減らすことができる。それがSDUIの最大のメリットです。

3. なぜGaudiyでSDUIを採用したのか

Gaudiyの話をすると、少なくとも現状はWebアプリケーション(PWA)のみで実装を進めており、ネイティブアプリの実装はしていません。またレコメンドエンジンに関しても、将来展望としてはありますが、現在は実装を行っていません。

それなのに、なぜSDUIを取り入れたのか。それは、顧客に提供するアプリケーションのカスタマイズ性を担保するためです。

こちらは、実際にGaudiyが提供している、各アプリケーションのホーム画面です。

実際のアプリケーション画面

同一ページ、同一ソースコードで実現していますが、表現がだいぶ異なっているのがわかると思います。

Gaudiyでは、ブロックチェーンを活用した「Gaudiy Fanlink」というプロダクトを通じて、さまざまなファン体験を総合的に提供していますが、そのコミュニティアプリは基本的に個別カスタマイズを行っていません。

プロダクトのめざす方向性や汎用性などの観点から、必要と判断された機能を追加する形で開発を進めていますが、クライアント企業によってはすべての機能を必要としていなかったり、段階的に機能を増やしていきたかったりするため、一部機能のみを提供する場合があります。

たとえば、

  • 提供する機能を増やしたい/減らしたい
  • ホームに表示するコンテンツを施策に合わせて変更したい
  • IPの世界観を壊さないような形で表現したい

といったクライアントの要求に対して、Bizサイドのメンバーがエンジニアを常に頼ることなく扱えるようにしたい。それを実現するための手段として、SDUIを実践することに決めました。

4. SDUIの実装方法

ここからは、GaudiyでどのようにSDUIを実装しているかをお伝えしていきます。

元々、BFF-Client間通信に使用していたApollo GraphQLを生かしつつ、Airbnbの実装例を参考にしながら弊社のユースケースに沿うように、レイアウト、UIパーツをGraphQL Schemaに落とし込んで表現しました。

これによって、

  • Fragment Colocationによるフェッチ最適化
  • 宣言的データフェッチによるReactの宣言的UIとの対応
    • 将来的に、宣言的なフレームワークを用いてネイティブに拡張可能

といった、データ効率とDXが高い開発が可能になりました。

では実際に、どのようなSchemaになったかを数点ご紹介していきたいと思います。

4-1. レイアウトのSchema

Gaudiyの現状のアプリでは、モバイルとデスクトップで、大きくレイアウトを変えることがあります。

具体的には、「モバイルで1列」「デスクトップで1列」「デスクトップで2列」の3パターンと、それぞれのレイアウトに対してトップに特別なコンテンツを置く・置かないの出し分けがあり、すべてのレイアウトは3 x 2=6パターンで表現されています。

デスクトップ・2列レイアウト

モバイル・1列レイアウト

これらを表現でき、また将来的にパターンが増えたり、タブレットなどに細かく対応する余地を持たせたSchemaとして、以下のように表現しました。

type LayoutsPerFormFactor {
  mobile: Layout!
  desktop: Layout!
}

union Layout = SingleColumnLayout | TwoColumnLayout

type SingleColumnLayout {
  mainTop: SingleSectionPlacement
  main: MultipleSectionsPlacement...
}

type TwoColumnLayout {
  mainTop: SingleSectionPlacement
  main: MultipleSectionsPlacement!
  sub: MultipleSectionsPlacement!
    ... 
}

抽象化したパターンがSchemaから把握できる形になっていて、わかりやすいかと思います。

4-2. UIパーツのSchema

前述したコミュニティごとの特性の違いから、ある機能単位での表示・非表示と、UI上の表現の違いを数パターンで表したいという要望がありました。

たとえば、以下のような同じ”トピック”機能に対して、ランキング形式でユーザー行動をより強く促す形と、フラットに表現する形で使い分けています。

ランキング形式で表現するパターン

フラットに表現するパターン

ここでは、機能ごとに出し分けする点と、機能ごとのpreview的に複数項目を表現する点から、機能ごとにセクションを分け、Arrayで子を持つように表現しました。

type SectionContainer {
  id: ID!
  sectionComponentType: SectionComponentType
  section: Section
    ...
}

enum SectionComponentType {
  TOPIC_CAROUSEL
  TOPIC_TRENDING
  ...
}

union Section =
    ChatSection
  | TopicSection
  ...

type TopicSection {
  title: String!
  items: [Topic!]!
    ...
}

type Topic {
  id: ID!
  label: String!
  description: String!
  imageUrl: String!
  createdAt: Int!
  isDisabled: Boolean!
  ...
}

機能の増減は Sectionで受け止め、機能ごとの見た目や並びのブレに関しては SectionComponentTypeで受け止めることで、unionが膨大に増えることを抑制しています。また、UIごとに必要になるfieldの違いに関しては、clientでのcolocationで定義することによって、over/under fetchが起きないようにしています。

このように、プロダクトの性質や、設計の採用目的によって重きを置くところが違うので、SDUIの思想をベースにしつつ、ユースケースにあった調整をした上で使用すると効果的だと考えています。

5. まとめ

今回はSDUIに関して、Gaudiyで実際にどう取り組んでいるかをまとめてみました。

特にフロントエンジニアの視点で考えると、Schema駆動でバックエンドと協力して設計・実装するのと同じくらい、もしくはそれ以上に、宣言するUIがデザイナーと認識の合った形になるように、こちらからヒアリングして提案することが大事になると思いました。

これから運用と拡張のフェーズを経て、引き続きSchemaを育てていきたいと思っています。

興味ある人がいれば、ぜひお話ししたいですー。

meety.net

Gaudiyの技術選定について知りたい方は、以下の記事をご参考ください。

techblog.gaudiy.com

UXの向上と開発の生産性を両立するスキーマ駆動開発

こんにちは!ファンと共に時代を進める、Web3スタートアップのGaudiyでエンジニアをしている勝又(@winor30)です。

Gaudiyのプロダクト開発において、最も大切にしていることの1つに、ファンダムな最高のユーザー体験を提供することがあります。

今回は、このファンダムなユーザー体験を提供しながら、プロダクトの成長速度を落とさないために取り組んでいる、UX中心のスキーマ駆動開発についてまとめてみます。

具体的な開発フローや工夫している点についてまとめたので、同じような課題を抱えるチームのご参考になれば嬉しいです!

1. 取り組みの背景

UXに直結するエンジニアリングの領域は、ユーザーが実際に触るフロントエンドが中心になります。そのため、よいユーザー体験を提供するには、フロントエンド開発にしっかりとリソースを割ける状態を作ることが大事だと考えています。

また、プロダクトが順調に成長するためには、よい体験を実現しながらも、バックエンドの領域に近いビジネスロジックを正しく実行したり、機能の拡張性や汎用性を担保することも必要です。

それらの観点から、第一にユーザー体験を、第二にプロダクトの成長速度を落とさないという目的から、スキーマ駆動開発を中心とした開発フローを取り入れました。

2. Gaudiyのアーキテクチャ構成

Gaudiyが提供するプロダクトのアーキテクチャは、下記のようなFE → BFF → BEのような構成になっています。

エンジニアによる実装範囲は特に明確になっていませんが、基本的にFEをフロントエンドエンジニアが、BEをバックエンドエンジニアが開発しています。BFFに関しては、集約や整形ぐらいのロジックしかないため、ケースバイケースで開発をしています。

https://cdn-ak.f.st-hatena.com/images/fotolife/g/gaudiy/20220310/20220310234806.png

3. UX中心のスキーマ駆動開発の概要

基本的には、下記4つの開発アクティビティを大切にしています。

  1. すべてのインターフェースをスキーマ駆動で開発する
  2. スキーマのズレを検知できるようにする
  3. システム全体の結合は開発初期の段階で行う
  4. FEのブロックにならないようにモックを返す

この意図としては、「FEのブロックにならないようにモックを返す」を基本方針とすることで、UXの向上や検証のために必要なフロントエンド開発に、最大限のリソースを割くことができる状態を作るためです。

また、開発初期の段階でBEやBFFをFEときちんと結合させ、実際に結合させた状態を先に作るようにしています。これによって、実際の挙動に限りなく近い状態にすることができ、UXの細かい部分をいつでも確認することができます。

このような状態を維持しながらBEの開発も行えるように、スキーマ駆動開発と継続的なズレの検知により、BEとFEが自律的に開発できる状態を作っていくことが大切だと考えています。

3-1. すべてのインターフェースをスキーマ駆動で開発

Gaudiyでは現在FE → BFFはGraphQLを、BFF → BEはRESTを利用してアプリケーション間の通信を行っています。

そのため、GraphQLではシンプルにGraphQLスキーマを、RESTではOpenAPIを利用してスキーマ駆動開発を行っています。

各スキーマについてはFE、BEのエンジニアが議論しながら作成し、それをPRに上げるところから開発を始めています。これによって、それぞれのエンジニアが自律的に開発でき、開発の生産性をかなり高めることができています

実際に行う際はLive Shareを利用して、同時編集でGraphQLのスキーマを作成することがおすすめです。

3-2. スキーマのズレを検知できるようにする

GraphQLやRESTは、必ずサーバー側とクライアント側が存在します。

そのため、いくらスキーマを利用していても、サーバーとクライアント側で異なるバージョンのスキーマが利用されてしまうことは起こり得ます。これが生じると結局バグが発生してしまうため、プルリクエスト時のCIなどでズレを検知できるようにすることが大切です。

Gaudiyではアプリケーションのコードをmonorepoに配置しており、クライアント側とサーバー側のコードがすべて同じRepositoryに存在しています。

そのため、GitHub Actionでスキーマに変更があったのか? また変更があった場合、生成されたコードでビルドは可能か? をチェックしており、それによってインターフェースのズレを検知できるようにしています。

下記はGithub Actionのサンプルになります。

name: Check OpenAPI Schema for bff

on:
  pull_request:
    types: [opened, synchronize]
    paths:
      # bffのコードが変更されたケースとbeのopenapi.yamlのスキーマが更新されたタイミングで発火する
      - 'projects/bff/**'
      - 'projects/be/openapi.yaml'

defaults:
  run:
    working-directory: ./projects/bff

jobs:
  check-openapi-schema:
    name: check code
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
...
      # openapi.yamlからコードを生成し、その後のstepでbuildをし、成功するかチェック
      - name: Generate API Client by OpenAPI Schema
        run: npx @openapitools/openapi-generator-cli generate -i ../../projects/be/openapi.yaml -g typescript-axios  -o ./src/datasources/be/openapi/
      - name: Check build
        run: npm run build

3-3. システム全体の結合は開発初期の段階で行う

開発初期の段階でFE、BFF、BEが通信を行っている状態をつくることによって、開発初期段階でモックを使っていたとしてもリアルに近いプロダクトをチームに提供でき、UXを高めることに繋げられます。

Gaudiyは、体験の検証やQAを頻繁にする組織なので、最新の状態で触ることのできる環境を常に用意することは非常に大事です。そのため、結合自体も早めに実施するということをやっています。

体験の検証やQAについては以下のblogに詳しく書いてあるので、ぜひ読んでいただければと思います。

techblog.gaudiy.com

また、経験上の話をすれば、開発終盤になってFEとBFFや、BFFとBEのI/Fが合わないみたいなケースがかなり多いと思っています。そのため、先にこのリスクを潰しつつ継続的に維持した方が、大きくズレる前に修正もしやすく、トータルのコストが低くなると考えています。

3-4. FEのブロックにならないようにモックを返す

開発初期でシステムの全体の結合を作るには、BEがデータを返却する必要があります。BEでデータを返却できるような状態を作るには、DBとの接続や別サービスとの接続、BEのビジネスロジックの実装などかなり工数がかかります。

しかし、BEの開発をFEに待ってもらうと、それこそUXの最大化に繋がりません。そのため、どういうデータを返すべきかをFEエンジニアと相談しながら、モックデータを返却するような形で、BEの初期は開発するべきだと考えています。

こうすることで、FEが自律的に開発することができ、よりUXの最大化に繋げやすくなります。

また、モックが返却するデータは、FEエンジニアや他のロールの人に相談しながら実際に利用するようなデータを作ることによって、初期段階でもプロダクトを触ったときの体験を少しでもリアルなものに近づけることができます。

4. 開発フローの具体的な流れ

前述の4つの開発アクティビティをベースにしつつ、基本的には下記のようなフローで案件の開発を行っています。

今回の記事を読んで興味がある方は、具体的にどのように開発しているかの解像度を上げるためにも、ぜひ読んでいただければと思います。

4-1. 開発の基本フロー

1:開発案件のストーリーマップとDDDを終わらせる

完璧なものを作る必要はありませんが、全体像やどういう方針で開発するかの認識を揃えておくことで、その後の開発の認識ズレがなく開発しやすいです。

2:FE・BEエンジニアでGraphQLのスキーマを設計し、プルリクエストを上げる

Live Shareなどを使って同時編集でスキーマを作成できるようにするといい感じです。

3:GraphQLスキーマからモックレスポンスを返すような実装をし、最速でFEと結合できる状態を作る

4:FEはGraphQLスキーマからQueryを生成したりして、実際に結合できる状態を作る

このとき、仮にBFFと結合できない場合は、MSW(Mock Service Worker)を利用するとBFFを待たずにlocal開発ができます。(ただし、開発・本番などのリモート環境では確認できません。)

5. BEはOpenAPIを設計し、OpenAPIに沿ったモックレスポンスを返すBEアプリケーションを開発する

6. OpenAPIスキーマからクライアントを自動生成させ、すべてモックレスポンスの状態でBFF ← → BE間を結合させる

7. BEを開発していく

4-2. 継続的に行っていくこと

また、継続的に行っていくこととして、実装するなかでスキーマが変わるときは、必ず担当の別領域の(BE → FE、FE → BE)エンジニアに報告するようにしています。

レビュアーにもアサインするとベターです。

5. さいごに

今回の記事では、Gaudiyで実践している「UX中心のスキーマ駆動開発フロー」についてまとめてみました。

この開発フローを取り入れてから、UXを高めるための開発にリソースを割けるようになり、開発チーム全体の生産性もかなり上がったなと感じています。よければぜひご参考になさってみてください。

もっと詳しく知りたい方がいたら、ぜひMeetyで気軽にご連絡いただけたらと思います!他社さんの取り組みもお伺いしてみたいです。

meety.net

ECDSA署名の数学的理解とCloud KMSによる実装

こんにちは!ファンと共に時代を進める、Web3スタートアップのGaudiyでエンジニアをしている椿(@mikr29028944)です。

先日、Gaudiyではサーバーサイドウォレットの構築やEthereumにおけるECDSA署名の実装を行いました。

そこで今回は、少しニッチではありますが「ECDSA署名」をテーマに、Gaudiyの事業背景から、ECDSAの数学的な処理とコードまでを、実例をふまえてお伝えしてみたいと思います。

はじめに断っておくと、僕は大学時代にzk-SNARKsの理論を研究していたため、代数学を学んだことはありますが、この領域における専門家ではありません。なので理解が誤っている部分があれば、ぜひご指摘いただけると嬉しいです。

Web3スタートアップで働くことに興味がある方や、ブロックチェーンを業務で扱うエンジニアの方にご参考になればと思い、詳しく書いていたら1万5千字を超えてしまいました…。

以下の目次から、興味あるパートだけでもよければご覧ください!

1. GaudiyがECDSA署名を扱う理由

Gaudiyでは、ブロックチェーンを活用したファンコミュニティサービスを開発・提供しています。

そのコミュニティでは、ユーザー体験を考慮して、これまではガス代のかからないプライベートチェーン上でNFTを発行していました。それを今回、ユーザー同士でアイテムのトレード(二次流通)を行ったり、NFTとしての価値を感じられる体験を提供するために、EthereumやPolygonといったパブリックチェーン上へと書き出す(ブリッジする)機能を実装することになりました。

しかし、すべてのデジタルアイテムを、ユーザーが好き勝手に書き出せてしまうのには問題があります

なぜなら、Gaudiyではマンガ、アニメ、ゲームなどのIP(知的財産コンテンツ)公式コミュニティをつくっており、そのコミュニティ内で配布されるアイテムもIPに紐づく画像などが多いため、著作権にも関わります。なので、あくまで「許可されているアイテムのみ書き出せる状態にしたい」というのが要件としてありました。

この課題を解決するため、サーバーサイドウォレットを作成して、Gaudiyからユーザーに対して「あるアイテムに対して書き出し可能な証明書」を発行することで、管理者の署名がないとアイテムを引き出せないようにする仕組みをつくることになりました。

ここで使われている「ECDSA」による署名や検証の方法について、Gaudiyでの実例も交えてお伝えしていきます。

2. ECDSA署名とは何か

ECDSA(Elliptic Curve Digital Signature Algorithm)とは、楕円曲線DSAとも呼ばれており、ビットコインやイーサリアムの文脈では特にトランザクションの署名に使われます。

▼こちらの記事がわかりやすいです。

zoom-blc.com

ECDSAを理解するには、前提知識として「楕円曲線暗号」を知る必要があるので、まずはその概要から説明していきます。(実例だけ知りたい方は、4. まで読み飛ばしていただいても大丈夫です。)

2-1. 前提知識としての楕円曲線暗号

2-1-1. ざっくりとした楕円離散対数問題

そもそも楕円曲線暗号とは、楕円曲線上で組み立てられた暗号プロトコル全般を指します。

近年、RSAに変わる公開鍵暗号として注目されており、RSAよりも短い鍵長で同じくらいの安全性を持つという特徴があります。実際に身近で使われている例としては、デジタル放送における映像コンテンツの著作権保護技術や、ICカード、SSL/TLSなどがあります。

後ほどより具体的な楕円曲線の話をしますが、この章では簡単に楕円曲線暗号の概念を解説したいと思います。

ある地図の上でゲームのキャラクターが歩き回ることを考えてみてください。

このゲームの世界では、地図から歩いて右方向に突き抜けても、反対側から同じ角度で出てくることにします。ゲームキャラクターが歩幅{P}で原点{O}から一定な角度をつけて歩くとします。2歩進むと{2P}の位置にいます。これを10回、100回…と続けても{10P,100P, 10^{100}P}と容易に位置を求めることができます。

しかしこの世界では、キャラクターが何歩歩いたかを忘れてしまった場合、今いる位置と自分の一歩から何歩歩いたかを求めようとすると、とても難しいことが知られています。一歩{P}が決まっている時に

歩数nから現在地nPを求めることは容易だが、現在地nPからnを求めることは難しい

という関係性が成り立ちます。一般に「有限体 \Bbb{F} _ {p}上の楕円曲線上の点 nP Pが与えらた時に nを求めよ」という問題を楕円曲線離散対数問題と言います。(地図を楕円曲線として変換しています)

また、この問題を利用して、楕円曲線暗号では nPを公開鍵、 nを秘密鍵として、「公開鍵から秘密鍵を現実的な時間で求めることが難しい」ことを安全性要件としています。

(以下の参考書から例を抜粋しています)

クラウドを支えるこれからの暗号技術 | 光成 滋生 |本 | 通販

2-1-2. 楕円曲線暗号

では実際に、代数的な側面から楕円曲線上の加算と楕円曲線離散対数問題について考えます。

 pを大きな素数として、有限体  \Bbb{F} _ p上の楕円曲線は、以下のような3次方程式で定義されます。

 4a^ 3+27b^ 2 \not = 0かつa, b \in \Bbb{F} _ pとする。以下では下式の楕円曲線を Eと表します。

 
\tag{*} y^2= x^3 +ax +b 

楕円曲線上の有理点の集合の全体は以下のように定義する加法について群をなすことが知られています。

曲線上の点 Aと点 Bを足した点 Dは、点 A Bを結ぶ直線が再び曲線と交わる点 C y座標の符号を反転した点で定義されます。点 A Bが同じ点である場合は、その点で接戦を引きます。また楕円曲線上の点として、無限遠点 Oと呼ばれる仮想的な点を定義します。さらに無限遠点 Oとある点 Pの加算結果は、点 Pになると定義します。

この時、曲線上の点 Gがある場合に、上記の方法でその点を k回加算する演算(スカラー倍算呼ぶ)と、自然数 kから点 W=k \times Gを満たす自然数 kを求める演算は、容易に求めることができます。一方で、このスカラー倍算の逆演算(2点 G Wから、 W = k \times Gを満たす自然数 kを求める問題)は楕円曲線離散対数問題と呼ばれ、自然数 kの桁数が大きくなるほど解くのが難しいと言われています。

まとめると、楕円曲線暗号は、この「楕円曲線上の離散対数問題」を用いて構成される暗号のことを言います。

次に、この楕円曲線上の離散対数問題を利用したECDSAによる署名と検証について考えていきます。

2-2. ECDSAによる署名と検証

アリスが自分の公開鍵を使って、ECDSAによる署名と検証を行うことを考えます。

  1. 公開設定:有限体 \Bbb{F} _ p上の楕円曲線 Eと点 P(素位数 lの巡回群の生成元)を選びます。また、ハッシュ関数 hを選びます。

  2. 鍵生成:まずアリスはランダムに a \in \Bbb{F}^{*} _ {l}を選び、 A = aPとします。このとき aをアリスの秘密鍵、 Aをアリスの公開鍵とします。

  3. 署名:ランダムに k \in \Bbb{F} _ {l}^{*}を選び、 kP x座標を rとします。平文 mに対して、以下のように s求めて (r, s) mに対する署名とします。以下では下式を s \equiv k^{-1}(h(m) + ar) \bmod lと書きます。

     \tag{1}  s \equiv {(h(m) + ar) \over k} \bmod l

  4. 検証: u \equiv s^{-1}h(m)  \bmod l v \equiv s^{-1}r \bmod lを計算し、 x座標に関して、 \bmod lで以下の式が成り立つことを検証します。

 
    \tag{*} uP +vA = k P

 A= aPより

 
    uP + vaP = kP

楕円曲線のスカラー倍算より

 
    (u + va)P = kP

 u, vより

 
    (s^{-1}h(m) +s^{-1}ra)P =  s^{-1}(h(m)+ra)P = kP

左辺に (1)を代入して、

 
    (k^{-1})^{-1}(h(m)+ar)^{-1}(h(m) +a r) = kP

となり、右辺の kPと一致することがわかり、正当な署名であることの検証ができました。では次に、ここで定義した変数や処理を用いて話を展開していきます。

3. EthereumにおけるECDSA署名

3-1. secp256k1と呼ばれる楕円曲線

先ほどECDSA署名の処理を構築した際に a, bなどの多数の変数を用いました。これらは、なんでもかんでも自由に決めていいわけではありません。脆弱性のあるパラメータで楕円曲線暗号を構築した場合、計算量が多項式時間で解けてしまう攻撃手法SSSA法、準指数関数時間で解けてしまうGHS法などのあらゆる攻撃手法によって、簡単に楕円曲線離散対数問題が解けてしまいます。こういった攻撃手法の適応条件に合わないようにパラメータを選定する必要があります。

NIST(⽶国⽴標準技術研究所)やSECG(The Standards for Efficient Cryptography Group)と呼ばれる楕円曲線暗号の商用的な標準仕様を査定している機関が、安全な楕円曲線暗号を作るための推奨パラメータを公表しています。詳しくはSEC 2: Recommended Elliptic Curve Domain Parametersをご覧ください。

Ethereumでは、SECGが推奨パラメータとして公表している中の一つである「secp256k1」を使用しています。この楕円曲線から、公開鍵や秘密鍵の生成がされています。パラメータ T = (p, a, b, G, n, h)はそれぞれ以下の値になり、 (*)で定義した楕円曲線に a, bを代入すると以下のような方程式になります。

 
y^2 = x^3 +7

後の章で nを用いた話があるのでここで説明しておくと、パラメーターに含まれる nとは、楕円曲線上の点 Gが与えられた時に n \times G  = Oとなる正の整数であり、この n Gの位数と呼びます。群の位数、元の位数の定義などはこちらの記事がわかりやすいです。

secp256k1でのパラメータ T = (p, a, b, G, n, h)は以下のような値になっています。

Recommended Parameters secp256k1

詳しくはこちらのRecommended Parameters secp256k1セクションをご覧ください。

3-2. EthereumでECDSA署名を扱う

では実際に、secp256k1の楕円曲線上のECDSA署名がどのように行われているのかをみていきます。

詳しいEthereumにおけるECDSA署名の仕様はこちらのEthereum Yellow Paperをご覧ください。

 
\text{signature} = (r, s, v)

EthereumにおけるトランザクションはECDSAによる署名がされ、32バイトの r値と s値、1バイトの v値を連結した65バイトを署名とします。 r値と s値に関しては先ほど記述した値になり、 v値に関しては x座標が rとなる点 Rを一意に定めるための値になります。

もし v 0の場合は R y座標の値が偶数、 v 1の場合は R y座標の値が奇数を表します。 v値に関しての詳細は、後述するEIP-155と一緒に説明します。またECDSA署名が有効な場合は、以下の条件が成り立ちます。

 
\begin{aligned}
0 < &r < \text{secp256k1n} \\ 
0 < &s < \text{secp256k1n}/2 +1
 \\ 
&v \in \lbrace 0, 1\rbrace
\end{aligned}

 \text{secp256k1n}とは、前述した n \times G  = Oとなる基点 Gの位数 nのことです。 rの取りうる範囲が 0から \text{secp256k1n}まで、 sの取りうる範囲が 0から \text{secp256k1n/2 +1}まで、 v 0 1の値を取ります。

 r \text{secp256k1n}までである理由はわかると思います。なぜなら、 \text{secp256k1n}は先ほどのECDSA署名の Gの位数 lにあたり、計算が \bmod l上でされているため、 r lを超えないことがわかります。ですが、 s \text{secp256k1n}までではなく、 \text{secp256k1n /2} +1までになっています。この理由は後述する「 sの反転」の章でEIP-2の概要説明と一緒に説明します。

また、署名から公開鍵の復元に関しては、平文 mのハッシュ値と r, s, vからリカバリできることがわかります。これに関しても後述します。

ここで記号を整理します。

  •  G:ベースポイント、基点と呼ばれる楕円曲線上の点(ECDSAによる署名と検証章の Pにあたる)
  •  h(m):平文 mのハッシュ値(ECDSAによる署名と検証章と同じ)
  •  k:署名ごとに異なるランダムで一時的な秘密鍵(ECDSAによる署名と検証章と同じ)
  •  n Gの位数 (ECDSAによる署名と検証章の lにあたる)
  •  r, s mに対する署名(ECDSAによる署名と検証章と同じ)

4. GaudiyではどのようにECDSA署名を行っているのか

ここからは、Gaudiyのユースケースを元にしたECDSA署名と検証フローを、実際のコードを添えて説明します。

4-1. 署名と検証の流れ

  1. digestと呼ばれるあるメッセージのSHA256ハッシュ化された値と、KMSに保存されているGaudiyの秘密鍵からKMSによる署名が生成されます
  2. この署名はASN.1と呼ばれる標準的なデータ構造から成るため、Unmarshalによって r, sをリカバリします
  3.  rに対する v値を計算し、 (r, s, v)を連結してそれをECDSAによる署名とします
  4. Ethereumのコントラクト上でこの署名が検証され、それによってリカバリされた公開鍵と署名を作成した公開鍵が一致するかを検証します

4-2. 詳細と実際のコード

4-2-1. Cloud KMSを使ったECDSA署名の構築

Gaudiyでは、秘密鍵の管理として、Cloud KMSを活用しています。

下記の記事にもあるのですが、プライベートキーを環境変数経由で渡す場合の問題点として

  • 秘密鍵を直接使えばプログラム以外の意図しない文脈で署名を行える
  • 秘密鍵を誰が使用したという履歴はどこにも残らない
  • 秘密鍵を直接持ち出せば退職者であっても署名を行える

が挙げられます。

zenn.dev

記事ではAWS KMS(Key Management System)を使ってこれらの問題を解決していますが、Gaudiyでは同じくEthereumの署名アルゴリズム、secp256k1をサポートしている Cloud KMS を使って解決することになりました。

Cloud KMSでは、非対称署名アルゴリズム、非対称暗号化アルゴリズムなどの様々なユースケースに対応できるように鍵のアルゴリズムを設定できます。

今回のケースでは楕円曲線署名を行いたいので、非対称署名アルゴリズムを選択し、曲線の種類をsecp256k1とします。Cloud KMS関する詳細はこちらをご覧ください。

Cloud KMSの鍵の扱いについて

まず最初にSignDigestという関数が署名全体の処理を行う関数になっています。引数には、署名者のアドレスとSHA256ハッシュ化させてメッセージを入れます。

このハッシュ化されたメッセージをAsymmetricSignRequestというオブジェクトを作成して、AsymmetricSignという関数を呼び、KMSによる署名を作成します。返ってきた値はASN.1の構造をしているのでrecoverRS関数を呼び、 r, s をリカバリします。

func (k *KMSSigner) SignDigest(ctx context.Context, address common.Address, digest []byte) ([]byte, error) {
  // KMSで署名を行うためのデータ
    req := &kmspb.AsymmetricSignRequest{
        Name: keyVersion,
        Digest: &kmspb.Digest{
            Digest: &kmspb.Digest_Sha256{
                Sha256: digest,
            },
        },
    }

    // KMSに保存されてる秘密鍵を用いて署名が作成される
    // この作成された署名は公開鍵を用いて検証することができる
    result, err := k.client.AsymmetricSign(ctx, req)
    if err != nil {
        return nil, err
    }

    // ASN.1データ構造のsignatureからrとsをリカバリする
    r, s, err := recoverRS(result.Signature)
    if err != nil {
        return nil, err
    }
 
    // 後半の処理
    ...
}

recoverRSの関数の中はこのようになっています。goの標準パッケージとしてasn1が提供されているので、単純にそのパッケージ内にあるUnmarshalを呼び出します。

// recover R and S from KMS signature
func recoverRS(signature []byte) (r *big.Int, s *big.Int, err error) {
    sig := new(struct {
        R *big.Int
        S *big.Int
    })

    _, err = asn1.Unmarshal(out.Signature, sig)
    if err != nil {
        return nil, err
    }

    // 処理
    ...

    return sig.R, sig.S, nil
}

4-2-2. sの反転について

急に sの反転についてとありますが、この話は、前述した sの取りうる範囲についてです。ECDSA署名が有効であるには、3つの条件がありました。そのひとつ s

 
0 < s < \text{secp256k1n/2 +1}

とすることが条件になります。EthereumのECDSA署名において、もし s \text{secp256k1n}の半分よりも大きい場合は s' = \text{secp256k1n} - s(この演算を「 sの反転」と呼ぶことにする)を計算し、この値を使ってECDSA署名を作る必要があります。

var (
    secp256k1N, _  = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
    secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
)

func recoverRS(signature []byte) (r *big.Int, s *big.Int, err error) {
    // 処理
    ...

    if sig.S.Cmp(secp256k1halfN) > 0 {
        sig.S = new(big.Int).Sub(secp256k1N, sig.S)
    }

    return sig.R, sig.S, nil
}

なぜ s 0 \lt s \lt \text{secp256k1n}ではダメなのでしょうか?これはEIP-2の中でこの問題が指摘されています。

github.com

EIP-2で言われている問題をそのまま翻訳すると

0 < s < secp256k1Nの範囲での任意のs値を持つトランザクションを許可すると、トランザクションの不正性が 懸念される。なぜなら、s値をsからsecp256k1N - sに反転し、v値を(27 → 28, 28 → 27)を反転させても結 果として得られる署名は有効である。

つまり、 sを反転して得られた s'から (r, s', v')を作成しても、異なる署名結果であるのに有効と判断されてしまうということになります。これについて実際に値を変化させて検証します。( v値については後述します)

前述の内容から sは以下のように求めることができました。

 
\tag{1}  s \equiv {(h(m) + ar) \over k} \bmod l

 sの反転」とは (1) sに対して、

 
s' = l -s

として s'とおきます。この s'から u'を求めると

 
\begin{aligned}
u' &\equiv s'^{-1}h(m) \bmod l  \\
&\equiv {1 \over l -s} h(m) \bmod l \\ 
&\equiv s^{-1} {s \over l- s} h(m) \bmod l \\ 
&\equiv  s^{-1} {s \over l- s} h(m) \bmod l - l (s^{-1}{1 \over l-s}h(m)) \bmod l \\
&\equiv s^{-1}{{s-l} \over l-s }h(m) 
\bmod l \\
&\equiv s^{-1} \lbrace -1  \times ({l-s \over l-s}) \rbrace h(m) \bmod l \\
&\equiv -s^{-1}h(m) \bmod l
\end{aligned}

となり、 u' uの関係は以下のようになります。

 
u' = -u

 vについても同様で、 v'= -vとなり、 u', v'から

 
\begin{aligned}
u'P +v'A &= -uP +-vA \\
&= -(uP+ vA) \\
&= -kP
\end{aligned}

点PとQのx座標の値は同じ

となるので、楕円曲線上の点のマイナスは y座標の正負をさせるだけで x座標の値は変わりません。

つまり、 (*)は両辺の x座標の点が同じかどうかを検証しているので、反転させた s'で評価をしても同じ検証結果が得られます。

そのためEIP-2では sの範囲を 0 \lt s \lt \text{secp256k1n}/2 +1に制限し、もしこの範囲外に sがある場合は、その署名を無効することでこのような問題を防いでいます。

4-2-3. v値について

 r, sをリカバリできたら最後に v値の計算です。 v値は r x座標の値とする曲線上の点を一意に定めるための値でした。 v \in { 0, 1}の範囲で (r, s, v)の有効性を検証し、有効性が示された時の v値がECDSA署名に含まれます。

今では v値が 0, 1の場合は古いバージョンとして存在していて、それに 27を足した 27, 28 v値として署名に含まれます。また v値についてメインネットやテストネットなどのネットワークを区別するために、chainIDを含めた v値がEIP-155で提案されています。今回はこの説明はしませんが、詳しく知りたい人はこちらをご覧ください。

 v 0 1のどちらかで正しい署名が作成できるので、2回検証を回して、それによってリカバリされるアドレスと署名者のアドレスを比較して同じ場合に、その値を v値にして最後に27を足して、署名の完成とします。

func (k *KMSSigner) SignDigest(ctx context.Context, address common.Address, digest []byte) ([]byte, error) {
    // 処理
    ...

    sig := make([]byte, 65)
    // r値
    copy(sig[:32], r.Bytes())
    // s値
    copy(sig[32:64], s.Bytes())
 
    for _, v := range []int{0, 1} {
        sigv := append(sig, byte(v))
        publicKey, err := secp256k1.RecoverPubkey(digest, sigv)

        if err != nil {
            return nil, err
        }

        verified := crypto.VerifySignature(publicKey, digest, sig[:len(sig)-1])
        addrA := common.BytesToAddress(crypto.Keccak256(publicKey[1:])[12:])

        if (verified && reflect.DeepEqual(addrA.Bytes(), address.Bytes())) {
            sig = append(sig, byte(v))
            break
        }
    }

    sig[64] += 27

    return sig, nil
}

以上、EthereumにおけるECDSA署名の作成方法について実際のコードを交えながら見ていきました。最後に、この得られた署名から公開鍵をどのようにリカバリするかを考えます。

4-2-4. ECDSA署名から公開鍵のリカバリ

署名の検証に関してはコントラクト上で行われます。ECDSA.recover によってアドレスが得られるので、得られたアドレスと署名者のアドレスを比較します。(この関数ではECDSA署名から公開鍵が得られ、内部でEthereumのアドレス形式に変換されています。)

address recoverdAddress = ECDSA.recover(digest, voucher.signature);
require(signerAddress == recoverdAddress, "address does not match");

実際にどのように公開鍵がリカバリされているのか見ていきます。

 r vから署名の際に選んだ公開鍵が一意に特定でき、それを Rとおく。

 
R= (r, y) = (u_1 + u_2a)P

 Rと署名とハッシュ化された平文 mに対して以下のように計算すると、

 
\begin{aligned}
Q &= r^{-1}(sR- h(m)P) \\
& = r^{-1}\lbrace s(u_1+u_2a)P - h(m)P \rbrace \\ 
&=r^{-1}\lbrace skP - h(m)P \rbrace \\
&= r^{-1} \lbrace {(h(m)+ ar) \over k } \times k P - h(m)P \rbrace \\
&= r^{-1}arP \\
&= aP
\end{aligned}

となり、公開鍵が導出されます。つまり、署名が正しければ導出された公開鍵と署名に使用した公開鍵が一致することがわかります。

実際 R x座標の rが満たすべき条件というのがあり、このSEC 1: Elliptic Curve Cryptographyで説明されています。詳しくはそちらをご覧ください。

以上より、ECDSA署名の作成と検証することができました。

5. さいごに

今回は、楕円曲線暗号の概要とCloud KMSを用いたECDSA署名の作成・検証方法をコードと一緒に見てきました。

冒頭にも書きましたが、大学で代数学を勉強した程度なので理解が間違っている部分等あるかもしれません。なので、もしそのような箇所があればぜひご指摘いただけると嬉しいです。

Gaudiyでは、このようにブロックチェーンが絡んだ実装もあれば、普通のWebアプリケーション開発の実装もあります。個人的には好きな分野なので、興味ある方いたらぜひお話ししたいです!

meety.net

ブロックチェーン知識は不要? 採用面談でよくあるQ&A集

f:id:hanahanayaman:20220401170428p:plain

こんにちは!エンタメ領域のDXを推進するブロックチェーンスタートアップ、Gaudiyでエンジニアをしているkei(@kei32bit)です。

Gaudiyの採用スタンスや具体的なスカウト手法についてはこれまでもご紹介してきましたが、今回は、実際のカジュアル面談や採用面談でエンジニアの方からよく聞かれる質問について深くフォーカスしていきたいと思います。

ブロックチェーンを扱っていると聞くと「何か特殊な経験が必要なのでは?」と思われる方が多いと思いますが、本記事では

  • 事前にブロックチェーンの知識を持っておく必要はない
  • ブロックチェーンを扱う企業にはレイヤーの違いがある
  • 重要なのはソフトウェアアーキテクチャの原則に基づいた開発経験

をお伝えできればなと思います!

1. ブロックチェーンって実際どういう風に業務で使われるの?

まずはじめに、実際のプロダクト開発でブロックチェーンをどう使っているの? という質問がよくあります。

Gaudiyのプロダクトは「ブロックチェーン技術を感じさせないような作り」になっているので、外から見るとピンと来ない方が多いかもしれません。

Gaudiyのプロダクト例 techblog.gaudiy.com

現在、GaudiyではプライベートチェーンでNFTを発行していますが、このNFTを発行する際にチェーンに書き込む処理の部分でブロックチェーン技術を利用しています。

が、それ以外のところでは、現状はブロックチェーンに関わる開発はあまり存在しません

というのもGaudiyはブロックチェーンを扱う企業ですが、アプリケーションレイヤーを扱っているので、ブロックチェーン自体の開発をごりごり推進する企業ではないからです。

f:id:kei32bit:20220401121006p:plain
ブロックチェーンにおけるアプリケーションレイヤー
ref. https://m13.co/article/crypto-and-the-consumer-the-road-ahead

アプリケーションレイヤー層にあたるGaudiyでは、ブロックチェーンはあくまでも数ある技術のひとつであり、ブロックチェーンを使わずに開発をするケースの方が多いです。

2. ソフトウェアアーキテクチャの考え方ってブロックチェーン業務でも使えるの?

結論からいうと、ソフトウェアアーキテクチャの原則に則った開発をGaudiyでは重要視しているため使えます。むしろ、開発する上でこちらの方がとても必要な考え方だと思っています

ブロックチェーン技術にはいくつか課題があるのですが、良いUXを提供するためにはソフトウェアアーキテクチャの原則に則った解決策を試行錯誤することが求められます。

具体例でいうと、以前、大量のNFTを発行するプロジェクトにて「NFTの発行が遅延しても、UXを損なわずにユーザーへのNFT配布をどう実現するか?」という課題に直面しました。

ブロックチェーンに書き込む(=NFTをmintする)際に遅延が発生するということはわりと有名な話ですが、UXを損なわずにユーザーがNFTを取得できる、という体験を提供するには工夫が必要でした。

この際のアーキテクチャ設計では「遅延配布」「非同期」といったワードをもとに、「NFTを書き出す処理だけTask Queueを使って非同期的に処理しつつ、ユーザーの受け取り画面にはメタデータ(画像など)を先に返却する」という解決策を選択しました。

ユーザーに対してはNFTの画像データを先に受け取ってもらうことでUXを向上させる、という目的を達成しつつ、非同期的に遅延配布することでNFTとして配布するという機能要件も達成しました。ですがここで、また新しい課題に直面しました。

それは、非同期処理を行うことで、nonce(ブロック生成時に必要な32bytesの数値。この数値が被ってしまうとブロックが生成できずに詰まってしまう)の制御が難しくなるという課題です。

これは非同期処理で制御せずに並列処理を行ってしまうと、nonce値が被ってしまう恐れがあるからです。

f:id:kei32bit:20220331210351p:plain
並列処理

この課題を抽象的に捉えると「直列処理」「排他制御」といったワードに変換できるので、当時は非同期処理の中で「直列処理で制御する」「並列処理を用いつつ、nonce値の予約を行うことで値の衝突を制御する」といった解決案を出すことができました。

f:id:kei32bit:20220331210412p:plain
直列処理

まとめると、今回の課題であった「NFTの発行が遅延しても、UXを損なわずにユーザーへのNFT配布をどう実現するか?」の解決策としては、以下の2点について抽象的に課題を捉える必要がありました。

1. NFTの書き出しが遅い => 遅延対応、非同期処理といった一つ上の抽象概念として課題を捉える

2. nonceの数値が被らないようにNFTをmintしたい => 並列処理ではなく直列処理で扱う、nonce値の事前確保を予約システムになぞらえて排他制御を加える、といった上位概念の解決策を当てはめてみる

具体例が長くなってしまいましたが、課題に対して事象を抽象化する思考とソフトウェアアーキテクチャに則った開発経験があれば、ブロックチェーン特有の技術課題にも対応できると考えています。

3. ブロックチェーン技術のキャッチアップってどれくらい必要?

さいごに、非常によくいただく質問ですが、結論からお伝えすると、入社前からブロックチェーンのキャッチアップは必要ありません

もちろん業務内容に応じてブロックチェーン技術の勉強が必要になりますが、Gaudiyでは以下のような文化があるため、業務内でのキャッチアップが可能です。なので、事前知識といったものは必要ありません。

3-1. ペアリサーチ

ペアプログラミングという開発手法がありますが、Gaudiyではリサーチ業務でもペアリサーチという形で取り入れています。

主に有識者とペアになってリサーチをするのですが、どんな内容でもラフに質問できるので、ブロックチェーンを学びながらリサーチを進めることができます。画面を共有しながら「なぜこの技術を調べてるのか」だったり「Twitterではこのあたりの有識者の方のコメントが勉強になる」だったり、自身のリサーチ力の基礎づくりにもなります。

以下の画像は、ペアリサーチをした際に異なるチェーン間でのスワップの仕組みについて順序を追って説明したときの図です。

f:id:kei32bit:20220331090818j:plain
例) NEARにおけるスワップの説明

Gaudiyでは現在、NFTをプライベートチェーンで発行・配布していますが、今後様々なパブリックチェーンにNFTを書き出す仕組みを進めており、「NFTを書き出すとは具体的にどういうことなのか?」を図を共有しながら説明したり質問を受けたりしました。

技術選定と同じで「なぜこの(ブロックチェーン)技術を使うのか」「メリット・デメリットはなにか」「ユーザー体験を損なわないか」といった議論は、ブロックチェーン技術を使った開発でも必須です。その前提知識の共有には、時間を費やしてしっかり理解する、というポリシーを持っています。

3-2. 超守-破離

「超守-破離」とは、Gaudiyの造語でCredo(行動規範)のひとつにもなっています。意味としては「まず先人の教えや原理原則に基づいた事例を学び、その上で新しいやり方がないか見つけ出す」といったものです。

  • 議論を始める前に、原理・原則や先人の事例をリサーチする「超守」を徹底する。
  • 過去の経験に過信せず、常に今のやり方を疑い、アンラーニングする。
  • 超守の上で、新しい概念や方法を導入し、積極的に新たな価値を生み出す。

どんなプロジェクトでも、新しい技術を使用する場合は必ず「超守」する時間をとるため、その中でブロックチェーン技術がどう使われるのかをキャッチアップできます。

具体例を挙げると、以前Gaudiyで「NFTのメタデータをどうやって生成するか?どこに保存するか?」を議論するケースがありました。 現在プライベートチェーンで発行・配布しているNFTを、今後パブリックチェーンに書き出す際に、メタデータ(画像など)をどこに置くかを前もって調査する必要があったからです。

この際も「超守」と呼ばれるリサーチの時間を取り、「そもそもNFTの資産性とはなにか?」や「オンチェーン/オフチェーンの定義とはなにか?」といった「NFTの資産性とメタデータがどう紐づくのか?」というテーマで調べました。

「超守」する際には必ず他メンバーとディスカッションしながら進めるため、何を調べれば良いのか分からないといった状況にならずに、リサーチの方向性について議論しながら進めることができます。

4. さいごに

今回は、ブロックチェーンを扱うスタートアップとして、エンジニアの方からよくいただく質問についてご紹介させていただきました。

冒頭でもご説明した通り、Gaudiyではブロックチェーンを扱っているものの、アプリケーションレイヤーでの開発を行っているため、ブロックチェーン自体をごりごり開発しているわけではないです。

ブロックチェーン特有の知識などは入社前には不要ですし、実際にエンジニアの約半数は入社後にペアリサーチなどを通じてキャッチアップしています。

ブロックチェーン企業の間にも扱うレイヤーの違いがあるため、一概には言えませんが、ブロックチェーン企業への応募に対してハードルを感じているエンジニアの方々にご参考になれば嬉しいです。

もっと詳細が知りたい、という方はぜひMeetyでお話しましょう!

meety.net

Gaudiyの技術選定などについて知りたい方は、以下の記事もご参考ください。

techblog.gaudiy.com

ブロックチェーンの勉強を始めてみたい方は、こちらもどうぞ。

techblog.gaudiy.com

Gaudiyのプロダクトってどうなってるの?

f:id:hanahanayaman:20220325141028p:plain

こんにちは!エンタメ領域のDXを推進するブロックチェーンスタートアップ、Gaudiyでプロジェクトマネージャーをしている小川(@zheye)です。

Gaudiyのビジョンや事業、技術などについてはこれまでもご紹介をしてきましたが、「実際、どんなプロダクトをつくっているの…?」という声をよく伺います。そこで今回は、Gaudiyの提供するプロダクトの中でも、コアとなる「ファンコミュニティ」にフォーカスしてご紹介してみたいと思います。

実際のUXやブロックチェーン技術などについても触れてますので、よければご一読ください!

1. なぜコミュニティをつくっているのか

Gaudiyでは、エンタメ企業の課題を解決するため、ファンの熱量を最大化するWeb3時代のファンプラットフォーム「Gaudiy Fanlink(ガウディ ファンリンク)」を開発していますが、そのコアは「ファンコミュニティ」です。

f:id:hanahanayaman:20220325114415p:plain

スマホ版ファンコミュニティ

Gaudiyのコミュニティの特徴としては、熱量の高いファンが集まり、ファンが創作・応援などの活動をしてIPを盛り上げ、それに貢献したファンにも還元される。その循環が回って経済圏を形成できることにあります。

Gaudiy Fanlinkのビジネスモデル

そのため、ファンの方々が集まり、自律的に創作・貢献・還元を行える場所として、居心地がよく・活動しやすいコミュニティづくりをめざしています。

私たちのミッションは「ファンと共に、時代を進める」ことです。将来的には、エンタメに限らず、なにかしらに熱量を持つ人々(=ファン)と一緒に時代を進めていきたいと考えていて、それを実現するための要素としてコミュニティがあります。

またビジネスモデルの観点では、IPコンテンツを保有する企業からお金をいただいています。熱量の高いファンを集めて、IP横断のユーザー基盤を持てるようにすることで、プロモーションやマーケティングへの活用や、デジタルコンテンツの販売、サービス連携などができるようになっています。

(今回は割愛しますが、エンタメ業界の課題背景については、以下のnoteをご参考ください。)

note.com

では早速、どんなプロダクトになっているのかをご紹介していきます。

2. ファンコミュニティの特徴とUX

ファンコミュニティの基本的な体験としては、IPコンテンツに関する投稿やチャットを通じて、ファン同士で気軽にコミュニケーションが取れることです。

その特徴としては「IP公式」「ファン主体」という点があります。

公式でありながら、「運営→ファン」という一方向的なコミュニケーションではなく、ファン主体で企画や創作などの活動ができ、双方向のコミュニケーションが生まれることが特徴です。

また、Gaudiyのバリューのひとつでもある「Fandom」な体験を提供することを大事にしています。いくつか具体例を出してUX面についてご紹介します。

2-1. ファン同士で助け合える「サポート広場」

コミュニティ内にある「サポート広場」は、ファンの方同士でわからないことや疑問などを質問し合える場所になります。

f:id:hanahanayaman:20220325114854p:plain

サポート広場での実際の投稿

通常、機能の使い方などの問い合わせには運営が対応すると思いますが、Gaudiyの提供するコミュニティではファンの方が質問に答えてくださることが多いです。

2-2. ファンの自律的な活動を支援

また、ファンコミュニティ内では、公式として認められた場で二次創作が推奨されているので、ファンアートSS(サイドストーリー)小説などがたくさん投稿されています。

f:id:hanahanayaman:20220325120506p:plain

こうしたファンアートを画集にして作者さんに届けたり、ファンの方が主催してくれたゲーム大会に協賛したりなど、ファンの方々の自律的な活動を運営としてサポートしています。(まだまだ手が回っていないところも多いので、より良い体験をお届けしていきたいです…!)

2-3. 新しいファン体験「ジェネレーティブアート」

約束のネバーランドの公式コミュニティでは、関連グッズを一定の金額以上ご購入された方に、自動で生成されるキャラクターと背景画像からなるデジタルアイテム(SNSで限定アイコンとして利用可能なもの)を配布するという取り組みを行いました。

ジェネレーティブアート自体はCryptoPunkなどさまざまな海外プロダクトで利用されていますが、国内ではまだ採用事例が少ないため、New Standardな取り組みだったかと思います。

マンガのキャラクターx背景の組み合わせで、世界に一つだけのユニークなものをいかに作れるかをめざして取り組みました。

f:id:hanahanayaman:20220325115032p:plain

技術的にはJavaScriptを用いて、草原やビーチ、洞窟などのテーマに沿って背景画像を組み合わせ、地形の高さや海や山などの素材の組み合わせのセットをランダムに配置し、キャラクターの画像を重ねることで実現しました。ジェネレーティブアートは一定のロジックで作成されているので、発行ごとに必ず再現性、唯一性が担保されます。

ジェネレーティブアートについては、ほぼ知見のない状態から1ヶ月弱で実装しなければならず、かなり大変でしたが、チームとしても一定の成果を出せたので自信につながりました。

ただ、キャラクター自体の着せ替えなどもっと表現を豊かにできる部分はあったので、生成までのユーザー体験も含めて今後より改善していきたいと思っています。

3. ファンコミュニティとブロックチェーン技術

また、Gaudiyの提供しているファンコミュニティには、DID(分散型ID)NFTといったブロックチェーン技術も活用されています。

3-1. 熱量を可視化するコインが流通する経済圏

コミュニティでは、投稿やイベント参加などのさまざまな貢献に応じて、独自のコインが手に入ります。そのコインを利用することで、IP独自のデジタルアイテムやNFTを購入することができます

f:id:xiaochuan:20220325185635p:plain

このデジタルアイテムには、公式が提供する電子書籍やイラスト原画集だけでなく、ファンの方が二次創作した小説やイラストなどもあります。

直近では現金(クレジットカード)によるNFT購入にも対応しており、過去には全く新しい方式でのNFTオークションも実施しました。

一般的なNFT市場では、価格のボラティリティが激しくファン体験を損なう可能性があるため、価格変動を抑えたり、転売を禁じたりするような仕組みも、今後必要に応じて導入していきたいと考えています。また、資産的な価値だけでなく、NFTを持っていると「二次創作の販売ができる」「特典や配当が受けられる」といったユーティリティ(実用価値)を増やしていく予定です。

※NFTに関してはQuorumというブロックチェーン基盤を利用しており、現時点ではパブリックチェーンに接続していない状態ですが、今後はコミュニティからウォレットなどの外部への持ち出しができるようにする予定です。

3-2. DIDによる「デジタルバックアップ機能」

これは、あるIPの音楽ゲームアプリがクローズすることになり、そのデータをコミュニティ内でバックアップできるようにしたものです。

長らく愛されてきたゲームが終わるのは、ファンや制作者にとってつらいことです。そこでゲームでの活動や思い出をなんとか残せないかとクライアントの方と相談し、ゲーム内のカードをNFTとしてコミュニティ内に移転し、各キャラクターのエピソード動画などを、コミュニティ内で閲覧できるようにする対応を行いました。

f:id:hanahanayaman:20220325120700p:plain

この裏側で使われているのが、DID(分散型ID)という技術です。

DID(こちらの記事で解説があります)を用いて、コミュニティのユーザーIDとゲームのユーザーIDを紐付け、コミュニティとゲーム間でのデータ連携ができていたため、今回の取り組みを実現することができました。

ゲームの終了自体はとても残念でしたが、ゲームの開発会社の方からもファンの方々からも喜んでいただくことができ、Fandomな体験を提供できました。

また、ゲームが終了しても、カードを所有できたり実際にプレイしていたことを証明できるという体験は、とても自律分散的だと思います。 将来的には、コミュニティ内でこのカードを所有することによるインセンティブや実用価値も付与していきたいと考えています。

4. さいごに

今回は、Gaudiyのコアプロダクトである「ファンコミュニティ」についてご紹介させていただきました。どのようなプロダクトをつくっているか、伝わったでしょうか?

Gaudiyではコミュニティを通じて、FandomでNewStandardな体験をさまざまに生み出しています。他にも、NFTやDIDを軸としたサービス展開も存在していますが、ベースとしてはこのコミュニティを基軸にしていくことは間違いありません。

熱量の高いファンの方に最高の体験を届け、時代を進める。そんな新しいプロダクトの開発をしてみたい方はぜひこちらからご応募ください。

www.wantedly.com

いろんな職種のメンバーがMeetyをオープンしてますので、こちらからでも!

meety.net