moodai
Moodai's Portfolio
HomeProjectsMemos

RootLensができるまで:試行錯誤の記録

RootLens
C2PA
Solana
cNFT

最初のアイデアは、とてもシンプルなものでした。SNSで流れてくる画像をスマホで撮影すれば、それが本当にカメラで撮られたものかどうかを確認できるプラットフォームを作る、というものです。

C2PA部分だけをサイドカー形式でクライアントからアップロードし、その証明書の倉庫をArweaveで構築する。そうすれば、トラストレスに証明書を世界に公開できるページが作れるんじゃないかと考えました。

しかし、すぐに壁にぶつかりました。現在のC2PAの仕様では、c2pa-rsに含まれるOpenSSLをWasmとしてクライアント側で動かす必要があります。でも、それは無理に近い話でした。サイドカーと画像の分離ができないし、新しい署名をC2PAに追加する処理はWebページ上では実行できないことがわかったんです。

ここで方針転換を迫られました。

本当はしたくありませんでしたが、画像や動画をRootLens側で保持してしまい、C2PAの署名処理には一切触れず、クライアントからアップロード・ダウンロードできるようにする方向を考えました。証明JSONのハッシュをチェーンで持つ、という形です。

でも、冷静に考えてみると、それではブロックチェーンを使う意味がほとんどありません。Web2で画像ストックサイトを作るのと変わらなくなってしまいます。

そこで、「証明書」という概念自体を、撮影者が主張できるようにすればいいんじゃないかと考え直しました。最初は、C2PA付きのカメラで動的に生成したQRコードを時間内にアップロードしてKYCを行う方法なども考えましたが、UXが悪すぎます。

PDAを実際にSolana Playgroundで作って、証明データをSolanaチェーンに刻む方法も試しました。しかし、1回数十円かかってしまう設計で、これでは実用的ではありません。

とにかく安く証明ハッシュを記録できる方法を探していくうちに、cNFTにたどり着きました。

最初は、なんでもいいから書き込めればいいと思っていました。cNFTは非常に安価に書き込めるので、いろいろ調べていくと、なんと「burn」という消せてしまう機能があることに気づきました。普通なら当たり前に知っていることかもしれませんが、初めてcNFTを触った僕にとっては、そこで初めて知った事実でした。

「これでは永続的に証明したいのに証明消えちゃうじゃん」と思いました。流動性をロックすればその問題を解決できることは知っていましたが、できればNFT自体の流動性を活かして、権利の移動を可能にしたい。画像購入の費用をそこに振り込めるようにしたい。そう考えて、ロックの方向性は諦めました。

さらに触っていくうちに、cNFTにはまともな生データを入れることはできず、URLを入れることしかできないことを知りました。

つまり、証明データの実体はArweaveに置き、cNFTはそのURLを参照する構造にせざるを得ないということです。

最初は「証明データ部分だけをArweaveに置いて、cNFTでそれを参照すればいいじゃん」と思ったのですが、深く考えてみると、この設計には致命的な問題がありました。

乗っ取りが可能になってしまう、という問題です。

具体的なシナリオはこうです:

  1. Arweave上にある証明データは誰でも見られる(公開データ)
  1. その証明データを参照するcNFTは、元コンテンツを持っていない人でも誰でも作れる
  1. もし正規の所有者がcNFT #1をburnで削除してしまったら?
  1. 第三者が作っていたcNFT #2が「最古の生き残り」として正当な所有者に見えてしまう

つまり、「このArweaveデータを参照しているcNFTのうち、どれが本物の所有者なのか?」を決める方法がないという問題です。

単純に「作成日時が古い方」というルールにしても、本物の所有者が間違ってburnした瞬間、権利が勝手に別のcNFTに移ってしまいます。

そこで思いついたのが、相互リンクという発想でした。

  • Arweaveの証明データに「正しい所有者のcNFTアドレス」を記録する
  • cNFTに「正しい証明データのArweave URI」を記録する
  • 両方が互いを参照していないと、無効とする

こうすれば、どちらか片方だけをコピーしても無効になります。両方向のリンクが一致して初めて、正当な所有権の証明になる。

「これで行けるんじゃないか!」と思ったのですが、すぐに気づきました。

これって鶏と卵の問題じゃないか、と。

  • ArweaveにcNFTアドレスを書き込むには、先にcNFTが存在している必要がある
  • でもcNFTを作るには、先にArweave URIが必要

どちらを先に作っても、もう片方を参照できない...。

その時、ちょうど時間があったので、その実現可能性について調べてみることにしました。どちらかのアドレスを事前に特定する方法がないか、まずAIに聞いてみたのですが、「そんなものはない」と言われました。

調べたところArweaveのアドレスにはランダム値(Salt)を含めるプロセスがあり、本当に無理そうでしたので、cNFTについて調べてみました。

すると、cNFTのAsset IDは決定論的に計算されて決まっていることがわかったんです。Merkle Treeの現在のnumMinted(これまでに発行された数)から、次に発行されるleaf indexを予測できる。そして、そのleaf indexとTree addressから、Asset IDを事前に計算できる。

必要な引数は全て手元にある。「これ、行けるんじゃないか!」とテンションが上がり、友達に連絡してしまったのを覚えています。

でも、冷静になってみると疑問が湧きました。もしmintに失敗したら、このロジックが壊れるんじゃないか、と。

AIは、「mint失敗の次のmintで、Merkle Treeの以前失敗した場所を使うから、予測は不安定だ」と主張してきました。たしかに、それだと使えないなと思って萎えかけました。

しかし、やっぱりAIが信用ならないと感じました。普通、オンチェーンのプログラムで失敗した箇所を後から特定して埋めるなんて複雑なことはしないんじゃないか、と。そこでしっかりコードを調べたところ、mint失敗に関わらず単純にカウントするだけの仕組みでした。

「これで行ける」と確信しました。

でも、ここで新たな問題に気づきました。本来、cNFTの発行は並列処理を前提として設計されているんです。複数のユーザーが同時にmintできるように作られている。

しかし、Asset IDを事前に予測するためには、「次のleaf indexは確実にこれだ」と言い切れる必要があります。つまり、並列処理を諦めて、直列処理に制御する必要があるということです。

この発想に辿り着くまでのプロセスが、本当に楽しかったです。技術的には単純なことかもしれませんが、「並列処理が前提の設計を、あえて直列化することで予測可能性を得る」という逆転の発想に気づけた瞬間は、パズルのピースがカチッとはまったような感覚でした。

実装では、BullMQのconcurrencyを1に設定して、一つのMerkle Treeに対して順番に処理していく形にしました。確かにスループットは落ちますが、検証時にはどのTreeから発行されたかは関係ありません。RootLensの署名さえ残っていれば、Treeを増やすことで対処できることにも気づけました。

あとは根幹部分のテストだけ済ませ、意外と時間が余っていたので、UXをとにかく意識して作っていきました。

途中まで、作ることばかりに集中していて、自分でも整理できていませんでした。このプロジェクトで何を証明できるのか、曖昧になっていたんです。そこで一度クリーンに考え直し、分析してみました。

すると、C2PAはそれ自体でトラストレスな事後検証を実現しており、真贋証明についてはブロックチェーンは関係ないことに気づきました。

後からクリアに考え直してみると、ブロックチェーンが生み出しているのは「真正性」ではなく「所有権」そのものだったんです。この気づきがあって、テーマを実装に合わせて軌道修正しました。

意外と、真贋証明にブロックチェーンを絡められなかったのは少し寂しかったです。でも、事実としてはそうだったので、所有権を押し出さないとSolanaハッカソンでは勝てないなと思い、方針転換しました。

色々触っていたら、結果的にマーケットプレイスができる構造にたどり着いていました。余った時間で、Solana Payを実装してみたり、ベクトル検索の知識を活かしてLens機能(AI画像検索)を軽く実装してみたりもできました。

動画制作も、前回のハッカソンの経験から、すごく大事だとわかっていたので今回は凝りました。Xの運用も、大事だとわかっていたので全力でやりました。フォロワーは1人も増えませんでしたが。

そんな2週間のハッカソンでした。


メモ一覧に戻る