ソフトウェアエンジニアなら3秒で理解できる NFT 入門

はじめに

NFT って何ですか?

ブロックチェーン上に記録された一意なトークン識別子をその保有者のアドレスと紐付ける情報、およびそれを状態変数として保持するスマートコントラクトのこと。

以上。

え、それだけ?

はい。

「デジタル資産に唯一無二性を付与するインターネット以来の革命」なんじゃないの?

これを読んでください:

speakerdeck.com

なるほど。ところで、この記事は何?

いま話題の NFT について、NFT の標準仕様である EIP-721 の仕様書と、それを実装しているスマートコントラクトのソースコードから読み解けることを解説する。一般向けの解説とは異なる視点から光を当てることで、ソフトウェアエンジニアに「あ、NFT って単にそういうことだったのか」と理解してもらえるようにすることを狙っている。

また、NFT がソフトウェアとして具体的にどう実装されているかを知ることは、今後、自分のやりたい事にブロックチェーン関連の要素技術をどう適用するか(あるいは適用できないか)を考える上で有用なはずである。

スマートコントラクトとは何か

ブロックチェーンとは何か」「マイニングとは何か」については、記事の最初に紹介したスライドの最初の章で解説しているので、この分野に詳しくない人はまずそちらを参照してほしい。

スライドでは、スマートコントラクトを「ブロックチェーン上でプログラムを実行する仕組み」と簡単に説明したが、ソフトウェアエンジニア向けにもう少し具体的に書くと、「現在のブロックに記録された状態を読み取って、それを元に新しいブロックの状態を計算して書き出すプログラム」だと言える。*1

簡単な例として、整数を 1 ずつカウントするスマートコントラクトを考えてみよう。コントラクトはオブジェクト指向言語のクラスに対応するもので*2、その中に状態変数とそれを操作する関数を定義できる。ここでは Counter コントラクトに _count 変数を定義して、increment() 関数の呼び出しでカウントする:

contract Counter {
    uint256 private _count;

    function increment() public {
        _count += 1;
    }
}

この increment() 関数がユーザから呼び出されると、以下のように動作する:

f:id:okapies:20220113005727p:plain

  1. 現在のブロック (#41) に記録された _count の値(ここでは 1)を読み出す
  2. 読み出した値に 1 を足す
  3. 新しいブロック (#42) に計算結果 (2) を書き込む

このように、コントラクトの状態変数はグローバルな(文字通り世界規模の)状態の一部としてブロックに記録され、関数を介して読み書きできる。つまり、スマートコントラクトとは「ブロックチェーンを記憶装置とする状態機械」のためのプログラムだと言える。また、コントラクトは自分自身の変数を操作するだけでなく、他のコントラクトの関数を呼ぶこともできる。

このコントラクトをブロックチェーン上で実行するには、それをコンパイルして得たバイトコードブロックチェーンに登録(デプロイ)する。登録が完了すると、そのコントラクトに対してユーザアカウント (EOA; Externally Owned Account) と同様にアドレスが払い出されるので、ユーザや他のコントラクトは、対象のコントラクトアドレスにトランザクションを送ることで関数を実行できる。この際、その計算ステップ数に応じて、ユーザは「ガス代」と呼ばれる手数料を支払う必要がある

なお、登録済みのコントラクトの修正や削除はできない。つまり、バグや脆弱性のあるコントラクトをデプロイしても後から修正できず、非常にリスクが高い。このため、本番のネットワークに登録するコントラクトは事前のテストや脆弱性診断が非常に重要とされている。*3

スマートコントラクトで独自の暗号通貨を発行する

ところで、スマートコントラクトを使うと独自の暗号通貨(トークン)を発行 (mint) できる。加えて Ethereum では、サードパーティ(取引所やウォレットなど)との互換性を保つために、独自トークンを扱うスマートコントラクトが従うべき仕様 (API) として EIP-20 を標準化している

独自の暗号通貨の立ち上げ、と言うと敷居が高く聞こえるが、実は OpenZeppelin などの OSS プロジェクトが EIP-20 の実装を提供しており、これを使えば誰でもオレオレ暗号通貨が作れる

また、その実装も特に難しいものではない。ポイントとなるのは以下の行:

contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;
    ...
}

つまり、ユーザごとの残高 (balance) が _balances 変数に書き込まれていて、対応するコントラクト関数(transfer(address _to, uint256 _value) など)の呼び出しで残高の移転が行われる。

独自貨幣の発行と流通という、一般に難易度の高い処理をここまで単純に記述できるのは、スマートコントラクトの独特な実行モデルによる。コントラクトは、マイニング競争でブロックの生成権を得たマイナーのコンピュータ上で一貫した順序付けの下に逐次実行されるので、そもそも並行性制御 (concurrency control) の問題が発生しないからだ。*4

スマートコントラクトで NFT を発行する

この EIP-20 を発展させたものが EIP-721いわゆる NFT (Non-Fungible Token) だ。Fungible は「代替可能」という意味だが、それを否定している ("Non-") ので「代替不可能」となる。

世の中によくある NFT の記事では、この命名から説き起こして「代替不可能で唯一無二性のある〜」と繋げるものが多いが、そのような説明はわれわれソフトウェアエンジニアには分かりにくい。やはり、実際のソースコードを研究するのが理解への近道だろう

というわけで、同様に OpenZeppelin が提供する EIP-721 の実装を見てみよう:

contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }
    ...
}

見てのとおり、この任意のトークン ID を保有者のアドレスに紐付ける _owners 変数が NFT の核心部分であり、逆に言うとそれ以上のものではない。つまり、アドレスに残高が紐付けられているのが EIP-20 なら、その残高であるトークンを tokenId で個々に識別する機能を持つのが NFT だ。

NFT に関する一般向けの解説を読むと「何やら高尚で難しいもの」という印象を受けがちだが、その実装を検討すると実に単純なプログラムであると分かる。

NFT はどのように作品と紐付けられ(ていない)か

ところで、先ほどのコードには作品とトークンを紐付ける NFT メタデータを保持するコードが含まれていなかった。その部分は EIP-721 では optional な仕様として定義されている。同様に実装を見てみよう:

abstract contract ERC721URIStorage is ERC721 {
    // Optional mapping for token URIs
    mapping(uint256 => string) private _tokenURIs;

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { ... }
    ...
}

このように、実は NFT のメタデータブロックチェーン上には記録されていない。代わりに、メタデータが置かれている外部の URI (tokenURI) を記録する。これは、コントラクトに書き込むデータ量が増えるほど「ガス代」が嵩むので、それを節約する目的がある。*5

この tokenURI“ERC721 Metadata JSON Schema” に従う JSON を指してもよい (may) とされており、以下に示す name, description, image の三つのプロパティが定められている:

{
    "title": "Asset Metadata",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "description": "Identifies the asset to which this NFT represents"
        },
        "description": {
            "type": "string",
            "description": "Describes the asset to which this NFT represents"
        },
        "image": {
            "type": "string",
            "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
        }
    }
}

これを見ると、意外なことに、EIP-721 では NFT を紐付けたデジタル作品を特定するための機械可読な記述は標準化されていないことが分かる。image がそうなんじゃないの?と思うかもしれないが、該当項目の説明に「幅 320〜1080 ピクセルで 1.91:1 か 4:5 の画像」と書かれている通り、これは明らかにサムネイルの URI を記述するためのものだ*6

では、実際に流通している NFT は、どのように自身と作品と紐付けているのだろうか。これは、NFT を実際に発行・流通しているマーケットプレイスに依存するというのが実情のようだ。例えば、NFT オークションサイトの最大手 OpenSea では、自サービスで解釈できる独自の NFT メタデータ仕様を定めている。

これを見ると、高解像度の画像を「NFT 化」するには、external_url に作品の本体をホストするサイトへの URI を書き込んでマーケットプレイスから誘導するしかない。以下の図は OpenSea のドキュメントより引用(赤丸は筆者):

f:id:okapies:20220103230145p:plain
OpenSea の画面とメタデータの対応付け(赤丸で囲った部分が external_url)

このように、標準仕様の NFT メタデータ作品データを特定するための情報を記述する方法が曖昧で、どちらかというと「マーケットプレイスが NFT のカタログページを作るための情報を提供する」という程度の位置付けに見える。例えば、紐付けた作品データの指紋(フィンガープリント)を記述する項目すら定義されていない。

これはつまり、購入した NFT 作品が別のものに改ざんされるリスクがあるということだ。例えば最近、暗号化メッセージアプリ Signal の創始者である @moxie 氏が、これが可能であると実証した経緯をブログ記事にまとめている。この記事では、OpenSea や Rarible などのマーケットプレイス上では普通のデジタルアートに見えるが、購入者が自分のウォレットからアクセスすると 💩 がデカデカと表示される NFT を作成して出品できたと報告している。*7

このように「デジタル資産に唯一無二性を付与する」と喧伝されている割には、価値の源泉であるはずの作品データ本体と NFT との機械的な紐付けがきちんと考慮されている様子がなく、なかなかに危うさを感じるのだが…。

NFT の本質は何か?

なぜ NFT のメタデータには作品データを特定する情報が含まれないのか。ここからは筆者の推測だが、その始まりにおいて NFT はゲームやデジタルトレーディングカードのための仕組みであり、作品データの管理は NFT のスコープではなかったからではないかと思う。

CryptoPunks の価値の源泉とは

一例として CryptoPunks を挙げる。これは 2017 年に始まった最も古い*8 NFT プロジェクトの一つで、多分に実験的な要素を含んでおり、後の EIP-721 の標準化に影響を与えた。

larvalabs.com

CryptoPunks はいわゆるジェネレーティブ・アート (Generative Art) で、アルゴリズムにより自動生成された 10,000 枚のドット絵のデジタルアイコンで構成され、その一枚一枚に通し番号 (#0〜#9999) が振られ NFT 化されている。また、ウェブ上でマーケットプレイスが提供されており、自分が保有するトークンを他のユーザと自由に取引できる。

開始当初はあまり注目を受けなかったようだが、2021 年に入った頃から突如としてセレブから多額の投資が集まり始め、3月にはそのうちの一枚である #78048 億円以上(!)という高額で取引されて話題となった

そんな CryptoPunks の実体はもちろんスマートコントラクトであり、そのソースコードは以下の GitHub レポジトリから確認できる*9:

github.com

一方、NFT 化によって唯一無二性と高い資産価値を与えられた CryptoPunks のアイコンデータはどのように管理されているのだろうか。驚くべきことに、今や億円単位で取引されるそれらのアイコンデータはソースコードと共に GitHub にアップロードされている。punk の全アイコンが集約された punks.png という画像ファイルがそれだ。

この画像ファイルのハッシュ値コントラクトにハードコードされているため、これを使って入手した画像の真正性を検証することはできる。だが、画像自体はただのデジタルデータであり容易に複製できるため、希少性や唯一性を保証することはできない。

では、CryptoPunks の保有者は何に価値を見出して高値をつけているのだろうか? 少なくとも、画像データ自体の希少性ではないのは確かだろう。その謎に答えているのが、以下の TechCrunch の記事(原題: "The Cult of CryptoPunks")だ:

jp.techcrunch.com

一言で言うと、punk 保有者たちのコミュニティが注目しているのはアイコンの属性だ。CryptoPunks のマーケットプレイスで閲覧できる各アイコンの詳細画面Attributes(属性)という項目を見ると、その punk の特徴(男性か女性か、帽子を被っているか、…)と、同じ属性を持っている punk の数が書かれている。記事によれば、この属性がトークンの価値に大きく影響するようだ。

f:id:okapies:20220116213501p:plain
"7-atty" と呼ばれる CryptoPunk 8348 の詳細画面

例えば、最初に紹介した 8 億円の punk10,000 体のうち 9 体しか存在しない宇宙人の punk である点が評価されたと言われており、画像データの希少性ではなく、発行者が割り当てた枠の希少性から価値が生まれるという構図が生まれている。他にも、punk の属性に基づく様々な価格決定メカニズムが働いている。記事によれば、例えば以下のような具合だ:

物事は常に予測できるとは限らない。パンクの属性として最も一般的な、イヤリングを付けたパンクは、最もレアな属性であるビーニー帽をかぶったパンクよりもはるかに低い価格で取引されている。しかし3Dメガネをかけた何百ものパンクは、数が少ない緑色のピエロの髪をしたパンクよりも高額のプレミアムを獲得する傾向にある。市場での勢いが不規則に増す属性もある。例えばここ数週間、パーカーを着たパンクの市場が特に過熱している。

...

30歳の暗号投資家であるマエガード氏はオーストラリアのブリスベンを拠点とし、クリプトパンクの価値に誰よりも投資している。彼は最近、特に珍しい「属性のない」女性のパンクを100万ドル(約1億1000万円)以上で販売した。彼は、最も希少なパンクの1つ(最も希少という人もいる)のオーナーでもある。このパンクは、7つのユニークな属性を持つことから「7-atty」という異名を取り、パンク伝説の聖地になっている。

イーサリアムの「最古のNFTプロジェクト」CryptoPunksをめぐる驚くべき熱狂 | TechCrunch Japan

この図式の成立に重要な役割を果たしているのがマーケットプレイスウェブサービスだ。実は、CryptoPunks の NFT には属性を表すメタデータは記録されていない。しかし、マーケットプレイスブロックチェーンから取得した情報に punk の属性情報を付加して掲示することでユーザがそのトークンの希少性を認識できるようになり、結果として売買の活性化に繋がっている。

このように、CryptoPunks のコレクションとしての価値の源泉は NFT でもアイコンの画像データでもなく、マーケットプレイスが提供するユーザ体験にその本質があると言えそうだ。

属性情報によってトークンの購買意欲を掻き立てる仕組みは、その後に立ち上げられた OpenSea にも引き継がれている。前述の、OpenSea が解釈できる独自メタデータの一つである attributes 属性がそれだ。OpenSea では attributesトークンの属性を文字列や数字で指定すると、それが作品ページ内で以下のようにビジュアル表示される。これは、CryptoPunks がマーケットプレイス上で提供していた属性情報をメタデータに埋め込むことで、より汎用的に利用できるようにしたものと考えていいだろう。

f:id:okapies:20220116212908p:plain
OpenSea で表示できる属性情報(OpenSea のドキュメントより)

最近、CryptoPunks を開発した Larva Labs はマーケットプレイスのアプリ自体を OSS 化し、Larva Labs 以外の人間が自由にホスティングできる「分散化」を推し進めている。この取り組みによって、たとえ今後、何らかの理由で公式のマーケットプレイスが閉鎖されてもユーザは将来に渡って punk の取引を続けることができるので、長期的な CryptoPunks の資産価値の安定性を高めることができる。*10

github.com

このことも、NFT の価値は単体では成立するものではなく、それを支えるパーツとしてマーケットプレイスが欠かせないことを示していると言えるだろう。

NFT はトレーディングカードである

CryptoPunks のこのようなあり方は、作品の美術的価値や文脈、そしてモノとしての希少性を評価するアートとはかなり性質が異なる。それは、どちらかといえば各カードに描かれたキャラクターの人気度や、その発行数量(コモンとかレアとか)によって価格が決まるトレーディングカードの文脈に近いと言える。*11

一般に、トレーディングカードやソシャゲで重要なのはコレクション性があることだ。そして、レアカードを買ったりガチャで SSR キャラを手に入れても、そこに描かれている絵柄やイラストの所有権を主張できるわけではないし、保有者もそこに価値を見出しているわけではない。つまり、この文脈において、トークンの保有者が作品の所有権や著作権を取得できるか、そのデータがどこに保存されているか、そもそもそれが唯一のコピーなのか、といったことは大して重要ではないということになる。

このことからも分かるように、CryptoPunks のような NFT は巷間で言われる「デジタルデータの資産化」ではなく、マーケットプレイスでの取引の集積の上に成り立つある種のゲームに見立てるべきであるように思う。何なら「人は何に資産的価値を見出すのか」ということを根底から問う、かなり興味深い社会実験であると評しても良い。例えば、かつて一斉を風靡した Cookie Clicker というゲームが、「人は何に達成感を感じるのか」を問うたように。

問題は、そこから生まれた NFT という仕組みが、アートという「モノを希少品として排他的に扱うことで価値を生み出す」ような文脈に持ち込まれたことではないか。

「顧客が本当に必要だったもの」を作る

最初に紹介したスライドでも述べたように NFT を「資産の所有権を表すもの」と見立てるのは法的な根拠がないだけでなく、技術的にも有効に保護する手立てがないため、無理があると言わざるを得ない。NFT ができるのは、あくまで「ユーザがトークン(≠資産)を排他的に保有していることを証明する」ことでしかないからだ。

「NFT」概念のブラックボックス化が孕む陥穽

この点を改良する試みがないではない。EIP-721 の規格化に関する issue では「NFT 化したファイルのハッシュ値の書き込みを必須にすべきだ」という問題提起がなされているし、メタデータの改ざんなどの問題に対処するために、EIP-721 の拡張として以下のような仕様が提案されている:

しかし、これらの提案に対するスマートコントラクト開発者たちからの関心は低く、標準化に向けた議論は停滞しているようだ。また、これらの課題を克服しても、「デジタルデータは複製可能である」という根本的な「欠陥」が解決するわけではない。

すでに見てきたように、NFT の技術的なアイデアブロックチェーン上にトークン ID と保有者のアドレスを紐付けるデータベースを作る」という非常に単純なものだ。基本的なソフトウェア開発の知識があれば誰にでも実装できるが、故に、それ単体では機能として不十分なユースケースも多い。

しかし、そういうものに「NFT」というクリプティックなラベルが貼られてそのままバズワード化したことで、その内実がブラックボックスになってしまった。そして、ソフトウェアの専門家ではない人たちに対して、あたかも「デジタルデータを資産化する夢の技術」として喧伝され、それが様々な問題を引き起こしているのが現状だろう。*12

「NFT」を超えてゆけ

個人的には、ブロックチェーン「特定の国家や企業に依存せずに、全世界から利用可能な記録を残せる」という性質を活用できる応用(アプリケーション)は何かしら有りうるだろうと思っている。また、ブロックチェーン以外の(中央集権的な)要素技術を使って実現できるアプリケーションでも、この領域のビジネスに資金が投下されてエコシステムが整備されつつあることを考えれば、それを当て込んだ技術選定は必ずしもナンセンスではない。

エコシステムの形成が進むと、それに伴って最適な要素技術と実現可能なソリューションは変わる。例えば、ユーザが自社サービスに関する何らかの「権利」を他のユーザと取引できるようにしたい場合に、それを仲介するマーケットプレイスを内製するよりも、NFT として実装し OpenSea のような人気のあるサードパーティに出品できる形で提供した方が、流動性などの面でより良い選択肢となる場合はあるだろう。

だがそれも、アプリケーションやサービスがユーザの課題をきちんと解決できてこそだ。率直に言って、自社で NFT を使ったサービスやプラットフォームを展開する場合を除き、現状で提案されてる NFT のソリューションが「顧客が本当に必要だったもの」である場合はさほど多くないのではなかろうか。そんな実態に目を背けたままバズワードとして「NFT」を振りかざしていると、いずれ重大な問題に繋がりかねない。

そうならないためにも、ソフトウェア開発者は NFT のソフトウェア技術としての本質を掴んだ上でソリューションを考えるべきだろう。

今のブロックチェーンやスマートコントラクトを巡る状況は、Web 2.0という言葉が叫ばれ始め、Ajax アプリケーションの端緒となった Google Maps などのウェブサービスが自身の機能を Web API として公開し、そうした APIマッシュアップしてサービスに仕立てることが流行りはじめた 2005 年頃と似ている。

言うまでもなく、その際に価値の源泉となっていたのはウェブサービスが提供するユーザ体験であり、Web API を実装して公開することはあくまでマネタイズ手段の一つでしかない。同じように、NFT はブロックチェーンアプリケーション同士の互換性を高めるために用意された API で、何かを NFT 化すること自体が価値を生み出すわけではない

本来、NFT は所有(モノ)ではなく体験(コト)の文脈に属する話で、ここで必要なのはサービス開発の考え方だ。だからこそ、そのサービスが顧客のどんな課題を解決するか、そして NFT を実装することでエコシステムからどのようなメリットを引き出したいか、そのことをよく考える必要がある。そして、きちんとその過程を経て生まれたソリューションは、もはや「NFT」というラベルを必要としないだろう

真に DX を実現するには

専門家ではない人々に向けた啓蒙も重要だ。近年、「モノからコトへ」とかデジタルトランスフォーメーション (DX) といったことが叫ばれているにも関わらず、結局は「デジタル所有権」のような「既成の価値観と親和性が高く、分かりやすい」説明が受け入れられる土壌があり、それが NFT を巡る混乱を引き起こしているように思う。

ある種のサイエンス・コミュニケーションとして、一般の人たちに教養として最低限のコンピュータ・サイエンスの知識を身につけてもらえるような活動は、今後ますます重要になっていくだろう。「デジタルには何ができて、何ができないのか」を多くの人が共有している状況になれば、社会の「デジタル化」はより加速していくはずだ。

*1:Ethereum のブロックチェーンは、このようなプログラムを実装しやすくするために「その時点のネットワーク全体の状態(のハッシュ値)をブロックに記録する」方式を採用している。一方で、冒頭に挙げたスライドでは、話を簡単にするため Bitcoin の「その時点で発生した取引をブロックに追記する」タイプのブロックチェーンについて説明している(この辺りの仕組みに興味がある人は "UXTO" で検索してほしい)。この記事では、前者の仕組みを前提として話を進める。

*2:Ethereum 用のスマートコントラクト記述言語 Solidity の場合。内部表現を考えると本来は OOPL でなくてもいいはず。

*3:有名なインシデントとしては、2016 年に、コントラクトの脆弱性をついた攻撃で 50 億円以上が盗まれた The DAO 事件などがある。

*4:逆に言うと、コントラクトの実行は原理的に並列化できないわけで、取引量がさらに増えた時にちゃんとスケールするかは疑問だが…。

*5:日付や時間帯によるが、Ethereum では一回の NFT の取引で数千円以上かかることもザラのようだ。

*6:EIP-721 にも「インスタグラムを参考にした」と書かれている。

*7:フィンガープリントについては、IPFS という P2P ファイル共有システムにアップロードするとハッシュ付きの URI が払い出されるので、それを使うのがベストプラクティスとされている。が、ここまで説明したような技術的背景を理解していない一般の購入者がそれを見分けるのは困難だろう。他にも、IPFS に保存したからといってファイルが絶対に消えないわけではない

*8:厳密には、これ以前から Ethereum 以外のネットワークで類似のコンセプトが試行されており、それを含めると更に古いものが存在する。

*9:このコードが実際に Ethereum にデプロイされ運用されていることは、Etherscan などのサイトで該当のコントラクトアドレスの情報から確認できる。

*10:もちろん Ethereum ネットワークが存続している限りは、だが。

*11:身も蓋もない言い方をすれば「お金持ちのためのソシャゲ」、ってことになるのだろうか…。

*12:「◯◯を NFT 化しませんか」という案件の勧誘は本当に多いようで、Doge こと「かぼすちゃん」の NFT 案件のように、ネットの片隅で愛犬ブログを開設しているだけの何も知らない一般人にまで声がかかる状況は、正直いかがなものかと思わざるを得ない。