拡張UTXOとは?スマートコントラクトでの役割

カルダノの特徴といえばPoS(プルーフ・オブ・ステーク)やスマートコントラクトが
目立って話題に挙げられますが、

実は拡張UTXOも凄く画期的なんです。

しかし、調べても何が凄いのがいまいちピンと来にくく
最も過小評価されている機能でもあります。

「PoSは現実世界だと定期預金」

「スマートコントラクトは現実世界だと契約」

の様にイメージが湧きやすいのに比べ、UTXOは日常生活にはあまり馴染みがありません。

そこで今回は拡張UTXOの魅力をお伝えすべく、調べた事をまとめましたので
しばらくお付き合いいただければ幸いです。

※ この記事は主に以下の資料を参考にまとめています。

拡張じゃないUTXOとは?

UTXOはUnspent Transaction Output(未払いのトランザクション結果)という意味で、
お金やトランザクションの管理モデルの一つです。

有名所でいうと、ビットコインはUTXOモデルを起用しており、
イーサリウムはアカウントモデル(銀行口座の様に残高をもつ)を起用しています。

単語からはイメージがつきにくいため、
まずはUTXOモデルがよく例えられる「現金での支払い」で説明します。

UTXOは現金払い

例えばサトシさんが1000円を持っていて、
自動販売機で110円のジュースを購入するとしましょう。

自動販売機には110円が支払われ、890円のお釣りが返却されますが、
これは「サトシさんに890円支払われた」とも言えます。

UTXOの例

このトランザクションのうち、払う前のお金は全てUTXOだと思ってください。

最初のUTXOである1000円は支払われた時点で「未払い」ではなくなるため、無効となります。
代わりに自動販売機には110円のUTXOが追加され、サトシさんには890円のUTXOが追加されます。

Directed Acyclic Graph

トランザクションの度に新たにUTXOが生成される(不可逆性)事から
UTXOモデルはDAG(有向非巡回グラフ)であると言われます。
DAGでは、ある始点から始まった矢印をあみだくじの様に辿った先に自分に戻らない、
つまり閉路がありません。

DAG

一つ一つの丸で描かれた頂点がトランザクションだと思ってください。

※ 似てますがブロックチェーンではありません

UTXOモデルにおいて、トランザクションには必ずアウトプットとインプットがあります。
DAG上では辺(矢印)がアウトプットで矢先がインプットです。

トランザクションのインプットとアウトプット

アウトプットには送金に関する情報があり、
インプットにはアウトプットに送金を完了させる「鍵」があります。



UTXOモデルは様々なブロックチェーンで使用されており、
それぞれ微妙に仕様が違ったりするのですが、
以下は代表としてビットコインにおけるアウトプットとインプットの説明をします。

アウトプットに含まれるもの

  • 値(送金額)
  • ScriptPubKey
    • 送金元アドレスのエンコーディング
    • 支払いを制御。正しいScriptSigが送信されるまで「値」をロック
    • 有効期限を設定する事で、成立しなかった際は自動的にトランザクションを無効にする

インプットに含まれるもの

  • トランザクションIDのハッシュ(デジタル署名済み)
  • トランザクション内のどの支払いかを示すインデックス番号(デジタル署名済み)
  • ScriptSig
    • 送金元の秘密鍵によって暗号化されたデジタル署名。
    • ScriptPubKey生成と一緒に作られる。

インプットには前のトランザクションへの「ポインター」としての役割しかありませんが、
実質インプットが分かれば紐づくトランザクションのアウトプットも取得出来ます。

UTXOトランザクション

この図からトランザクションはインプットとアウトプットで構成されているのが分かると思います。
そして図の様に、トランザクションは複数のインプットとアウトプットを持つ事もあります。

アウトプットにあるScriptPubKeyは「鍵穴」のような物だと思ってください。
ScriptPubKeyは、最初は「施錠」されていていますが、
対となるScriptSigという「」によってのみ「解錠」されます。

解錠されると、アウトプットのUTXOは未払いから支払い済みになるため、
新たにUTXOとしてアウトプットが作成されます。

基本的にどのようなトランザクションも同じですが、
例外として、最初のビットコインを発行する際のCoinbaseトランザクションでは
「前のトランザクション」がないため、インプットのトランザクションIDやインデックス番号は
固定です。

ScriptSigも参照するScriptPubKeyがないため、自分の名前だったり好きなメッセージを入れるそうです 😀

UTXOが好まれる理由

ここまでの話から、UTXOは銀行口座とお金の管理方法が全く異なる事が分かります。

UTXOモデルにおいて、「お金を持つ事」とは参照できるUTXOがある事を意味します。
コイン枚数をデータとして保持しているわけではありません。
また残高とはUTXOの総和で、ウォレットの残高表示も全てのUTXOを毎回足し算しています。

現金払いとは違い、

  • トランザクションの度に全額を送信
  • トラッキング可能な追加情報が含まれている
  • トランザクションの度に古いUTXOを破壊して新しいUTXOを作る

イメージしづらいですが何となく伝わりますでしょうか。

UTXOモデルは感覚的に理解しにくい反面、以下の理由で好まれて導入されています。

  • 全てのトランザクションは前のトランザクションへ限定的に辿れる
  • トランザクションの正当性チェックはノードだけで完結できる
  • トランザクションのアウトプットはインプットによってのみ決まる(関数型のように決定論的)
  • トランザクションの最終結果は順番に依存しない(並列処理で高速化)

ただ、ビットコインの様に単純な送受金だけを行うユースケースには非常に有効ですが、
特定の条件の時のみ支払うスマートコントラクトを実現するには不向きでした。
※ ただもしかたらTaprootアップデートで可能になるかもしれません。

そこでイーサリウムが導入したのがアカウントモデルです。

UTXOだけではスマートコントラクトに対応できない

スマートコントラクトを実現する為には、複数のデジタル署名が必要になります。

残念ながら従来のUTXOではオフチェーンでしかマルチシグネチャを実現できません。

そこでオンチェーンのマルチシグネチャ取得を実現するため、
及びシグネチャ取得後のロジックをコードで制御するために
イーサリウムはUTXOモデルの代わりにアカウントモデルを起用しました。

アカウントモデル

アカウントモデルは銀行口座のトランザクションと同じなため、
直感的に理解しやすいと思います。

UTXOモデルと同じくトランザクションの認証にはデジタル署名を使いますが、
主な違いとしては以下が挙げられます。

  • 残高をデータとして保持
  • 任意の値を送金できる(お釣りの概念がない)
  • トランザクションの検証プロセスでは十分な残高があるのを確認
  • 送り元は残高をマイナス。送り先は残高をプラス。

アカウントモデルの問題:スケーラビリティ

トランザクションが発生すると送り元残高は減り、送り先残高は増えますが、
このトランザクションを故意に量産させて送り元残高がゼロになるまで繰り返すリプレイアタック
というものがあります。

イーサリウムでは、この攻撃への対処として、各ノードで全ユーザーアカウントの
全トランザクションのスナップショット(状態の記録)を保持しています。

悪質なトランザクションによって汚染されてもナンス値から修正出来るようにする為の設計ですが、
ノードへのバリデーション負荷がユーザー数とトランザクション数に比例して大きくなるのが問題です。

アカウントモデルは私たちが慣れ親しんでいるため直感的に理解しやすい反面、
ネットワークのスケーラビリティとプライバシーの観点からベストなソリューションではない気がします。

アカウントモデルの問題:複雑なコーディング

ユーザーのアカウントとは別に、スマートコントラクトには専用のアカウントがあります。

ビットコインのUTXOモデルでは、スマートコントラクトの様にトランザクションの完遂に
複雑な条件付けするのは不可能なため、イーサリウムでは

スマートコントラクトに参加している契約者たちのアカウントの「状態」をスマートコントラクト上で共有
(グローバルステート)する概念を導入する事で、様々な契約条件に即座に対応出来るよう設計しています。

この設計を実現する代償としてスマートコントラクトのコードは一層複雑になり、
複雑化したコードが誰も気づけない潜在バグを生み出し、その結果、悪名高いDAOのような
スマートコントラクトを再帰的に呼び出せるセキュリティ上の脆弱性をついた攻撃を想定出来ませんでした。

辿り着いた”答え”

UTXOモデルのシンプルさと、アカウントモデルのプログラムによる表現の自由度、
この2つを兼ね備えた最適モデルを追求した結果、

カルダノはUTXOをプログラム出来るように”拡張”しました。

拡張UTXO

UTXOとスマートコントラクト

UTXOモデルはアカウントモデルより高いスケーラビリティとプライバシーを提供します。
しかもトランザクションの度に新たに生成され、前のUTXOは無効になるためロジックも単純。
結果、UTXOの正当性チェックも最新(Unspent)だけ検証すれば済みます。

カルダノの拡張UTXOは従来のUTXOと違い、スマートコントラクトをサポートするよう
以下の要件を満たしています。

  1. コントラクトの「状態」の維持ができる(ステートフル)
  2. 同一コントラクトで発生するトランザクションは全て同じコントラクトコードを実行する連続性を持つ

拡張UTXOに含まれるもの

従来のUTXOのアウトプットでは、値とバリデーター(ビットコインではScriptPubKey)が
内包されていましたが、拡張UTXOではスマートコントラクトがバリデーターの役割を担い、
更にDatum(英語でDataの単数形)を追加しています。

具体的には以下を含みます。

  • アセット
  • スマートコントラクトスクリプト
  • オンチェーンデータ(Datum)

アセット

アセットとは同じトークンの集まりの総称で、lovelaceに対するadaがこれに当たります。
他にもカスタムトークンやNFTもアセットに含まれます。
カルダノのUTXOでは、これらのアセットをまとめたトークンバンドルとして送ります。
つまり、

従来のUTXOでは財布(全財産)ごと渡しているのに対して、
カルダノのUTXOでは財布・金券・株券・権利書等、全ての資産を渡しているイメージです。


※ ちなみにビットコイン等の第一世代ブロックチェーンでは、「トランザクション」は主にコインの送信
として使われましたが、多くのブロックチェーンではもっと広い意味で使われます。
カルダノにおけるトランザクションとは、何かの「値の所有権の移動」として留めてください。

スマートコントラクトスクリプト

スマートコントラクトスクリプトは高水準のバリデーターだと思ってください。
従来のUTXOではScriptPubKeyや公開鍵のハッシュのみ用いてアウトプットの値を使用出来るか
判断していましたが、拡張UTXOではスマートコントラクトスクリプトが「鍵穴」の役割を担います。

そのため、スマートコントラクトを自動で契約を遂行するエンティティとして考えるよりも、
指定通りアセットを守ったり渡したりするハイレベルな「ガード」として捉えた方が
ニュアンス的には近いかもしれません。

Datum

Datumにはスマートコントラクトがアセットを「アンロック」「支払い」「解錠」出来るかを
判断するのに必要なデータが含まれています。

そしてUnspenがSpentになり、新しいUTXOを作る時は前のトランザクションが引き継がれます。
こうする事で、「過去のトランザクションは全て同一のスマートコントラクトの
バリデーションルールの下検証済み」である事を保証してくれます。

拡張UTXOトランザクションの流れ

スマートコントラクトアドレスにトランザクションがインプットとして送信された時、
紐づくアウトプットのスマートコントラクトスクリプトが実行されます。
以下はスマートコントラクトが参照するUTXO内データです。

  • アセット
  • Datumのオンチェーンデータ
  • 必要に応じて他のインプット由来のアセットやDatum

スマートコントラクトのバリデーターに変数としてアセット、Datum、トランザクション、
そして最後にデジタル署名等の「鍵」を与えた後、
「OK」であればアセットがアンロックされます。

トランザクションの状態遷移

「特別な条件を満たした時にアセットを支払う」という機能をスマートコントラクトに
持たすためには、「今の状態」が条件を満たせるか判断する必要があり、「最新の状態」を
保つためには例えばアカウントモデルにおける残高が変化した時に「状態を遷移」させます。

アカウントモデルとは違い、拡張UTXOで動くスマートコントラクトでは状態遷移の度に
プログラム「関数」を呼び出す必要はありません。従来のUTXOと同じく、拡張UTXOでは
トランザクションで支払われる時、古いUTXOは破棄され新しいUTXOが作成されます。

つまり、能動的に何かしなくてもトランザクションと連動して「最新状態」になります。

UTXOの状態はDatumで表現していて、Datum内のデータを変更するトランザクションを
送信すると新しいUTXOが作られ、状態が遷移します。

UTXOは1度しか使えない事から、1ブロックにつき1つのトランザクションしか含められないのでは?
そうなるとスマートコントラクトは「同時に」1人のアクター(契約者等)としかインタラクト出来ないのでは?

と思われるかもしれません。

まず、カルダノのdAppsは複数のトランザクションをブロックに含めるのが前提です。

実際のスマートコントラクトのブロックには数百もの単純なトランザクションと
いくつかの複雑なスクリプトで構成されています。確かに拡張UTXOでは、1回使用したら
2度と使えないですが、解決策として「複数のUTXO」を用いて「スマートコントラクトの状態」を説明する
デザインパターンの設計がとても重要になります。

つまり、

  • 同時性
    • 複数のアクターが互いに影響せずに同じタスクを実行できるか
  • 並行性
    • 同じタスクを同じタイミングで互いに影響せず実行できるか

高い同時並行性を兼ね備えた設計をする事が、例えばミリ秒単位でデータが変動するDEX等の
DeFi系dAppsを開発する上で満たすべき要件の一つなるでしょう。

拡張UTXO単体では同時並行性を許しませんが、カルダノのスマートコントラクトは
複数のUTXOとインタラクトするため、決してカルダノが同時並行処理をサポートしないわけではありません。

スマートコントラクトを用いたdAppsの設計例

読み取り専用のデータインプットの導入

例えば為替レートのようなミリ秒単位で変動するデータを拡張UTXOのDatumに含めた場合、
トランザクションが爆発的に増えてしまうのではないか?という疑問が浮上します。

この問題を解決する為に一部のdAppsでは「読み取り専用インプット」を利用しています。
UTXOのDatumにデータを置くのではなく、dAppsから「読み取り専用インプット」を
参照する構造にします。

  1. 現実のデータをインプットから参照可能にする
  2. dAppsはデータのインプットを参照するだけ
  3. UTXOの状態が遷移していない為、余計なトランザクションコストが発生しない

オラクルデータとして現実世界の情報をオンチェーンに提供されていれば、値の変動に
依存せずトランザクション数も大幅に減らせます。余分なUTXO消費も発生しません。

これらのオラクルデータは並行して複数のUTXOを作成しているため、同時に複数のdAppsから参照できます。

スマートコントラクトのタスクをサブセットで細分化

dAppに一つのスマートコントラクトに全てのトランザクションを順番に連続してやらせるのではなく、アクターごとに
別々のスマートコントラクトを用意し、それらの最終的なUTXOを親スマートコントラクトで集計分析するという
設計があり、クラウドファウンディングやトーナメント形式のdAppで応用出来ます。

それぞれの子UTXOはお互いに依存せず、並行して進むためdApp全体のスループット向上に繋がります。

もしスマートコントラクトのバリデーションが他のスマートコントラクトに依存する場合、
例えば総当たり戦で勝ち点によって試合相手が決まるユースケースの時などは、
それぞれの試合結果を「読み取り専用データインプット」の様に提供すれば、
他の子スマートコントラクトのトランザクションを阻害しなくて済みます。

データインプットを参照できるという事は、状態をスナップショットとして記録も出来ます。
もしかしたら任意のdApp関連のUTXOの検証やスナップショット作成による報酬がそのdAppから与えられる
ようになるかもしれませんね。

バッチプロトコルと呼ばれるデザインパターンですが、長くなるため詳細はここでは割愛します。

手数料の正確な予測が可能

イーサリウムでは、EVM(Ethereum Virtual Machine)上で走るスマートコントラクトでは
参加しているユーザーアカウントの状態遷移を監視しています。

参加者のトランザクションの活発度によって、スマートコントラクトアカウントの
状態遷移も活発化するため、スマートコントラクトの「費用」が予測できず、
実際にSolidityがスマートコントラクトを実行して初めて手数料が分かります。

最悪コントラクトの途中で手数料がロックした資産よりも上回る場合、
コントラクト自体がキャンセルされます。
しかし、トランザクションは発生し、マイナーに報酬も支払われるため手数料は返ってきません。

更にユーザーはトランザクション費用を設定出来、マイナーは報酬還元率が高いため
高い手数料のトランザクションから優先的にマイニングする傾向があります。
この事も含めてガス(トランザクション費用)をより高いものにしています。

複数のスマートコントラクトの連携を必要とするユースケースだともっと費用は重なります。

拡張UTXOモデルはインプットによってのみアウトプットが決まる、つまり決定論的であるため
スマートコントラクトを実行する前に、より正確に手数料の推定が可能です。

カスタムトークンは全てネイティブトークンとして扱える

こちらは拡張UTXOの特徴ではないのですが、スマートコントラクトを語る上で
とても重要ですので少し触れます。

イーサリウムではカスタムトークンを発行する時、ベースとなる規格ERC-20を基準としています。
ERC-20はFungibleなトークンな為、NFTには別の規格ERC-721を基準ととして発行します。

こちらのカスタムトークンはスマートコントラクト上でしか送れない上に、
発行するための規格コードはスマートコントラクトにコピー&ペーストする必要があります。

つまりとても非効率的で余分な費用もかかる上、人的ミスも起こりやすい。

ERC-20をETHの様に扱うことは出来ませんが、カルダノのカスタムトークンは
ネイティブトークンであるためADAの様に扱えます。

ADAとネイティブトークンの違いは

  • ADAはミント(発行)する事もバーン(破棄)する事も出来ない
  • ADAは手数料の支払いや報酬の支払いとして使用出来る
  • ADAはUTXOの最低預り金として利用出来る

多少の違いはありますが、基本的にネイティブトークンはADAと同じ様に扱えます。

つまりUTXOのアセットとして、ADAと同じ要領でカスタムトークンやNFTを送れるため、
そもそもトランザクションにスマートコントラクトを必要としません。

結果、軽量かつ効率的で手数料も大幅に削減できます。

以下はERC-20とネイティブトークンの違いです。

 ERC-20ネイティブトークン
ブロックチェーンイーサリウムカルダノ
トークン発行カスタムトークンの規格
元のコードをコピペして
新しいトークンを発行する
規格ではない。
発行に必要な機能は
"台帳"に備わっている
ミント/バーンするのに
スクリプトを必要とする
ミント条件を変更可能
送るのにスマートコントラクトを必要とする
他のスマートコントラクトで使う為に追加サポートが必要
他のトークンと一緒に送れる
トークン送信方法ERC-20のテンプレートを
コピペ
"台帳"が行う
送信ロジックをカスタム出来る
トークン送信に別途手数料が発生する
トークン送信をトラッキング
するために追加でイベントハンドラーが必要
NFTとして発行別の規格を使う必要がある
メタデータの参照法稼働中のスマートコントラクトからオフチェーンメタデータサーバーから

さいごに

AlonzoハードフォークはPlutus1.0を導入しましたが、エコシステムの成長はまだまだこれからです。

カルダノのスマートコントラクトは従来のアカウントモデルとは全く違うため、従来のdAppのアーキテクチャを
そのままカルダノに移行は出来ません。開発者には主に高い同時並行性の実現と意図しない依存関係を避ける
設計が求められます。

既に数多のプロジェクトがPlutus環境で開発されており、これからもハッカソンなどの開発者イベントや
カタリストを通してカルダノエコシステムは益々の成長を期待できることでしょう!

最後までご高覧いただきありがとうございます!