ブロックチェーンの二つの取引モデル:UTXOとアカウントモデルの技術比較
はじめに:ブロックチェーンにおける「状態」の管理
ブロックチェーンは、単にデータを記録するだけでなく、その上の「状態」を管理する分散型システムです。ここで言う「状態」とは、誰がいくらのデジタル資産を持っているか、スマートコントラクトがどのような状況にあるか、といった情報を指します。この状態をどのように記録・更新していくかは、ブロックチェーン設計における重要な要素の一つです。
特に、デジタル資産の所有権や移転を記録する「トランザクション」の処理方法には、主に二つの異なるモデルが存在します。それが「UTXO(Unspent Transaction Output)モデル」と「アカウントモデル」です。これらのモデルは、ビットコインやイーサリアムといった代表的なブロックチェーンで採用されており、それぞれの特性を深く理解することは、ブロックチェーン技術全体を把握する上で不可欠です。
プログラミング経験のある方であれば、既存のデータベースシステムにおいて、データをどのように構造化し、トランザクションを処理するかという設計の重要性をご存知でしょう。ブロックチェーンにおけるUTXOモデルとアカウントモデルも、分散環境下での「状態」の一貫性と正確性をどのように担保するか、という点において似た設計思想を持ちつつも、そのアプローチは大きく異なります。
本記事では、これら二つのモデルの技術的な仕組み、それぞれの長所と短所、そしてどのようなブロックチェーンで採用されているかについて、詳しく解説します。
UTXO(Unspent Transaction Output)モデルの仕組み
UTXOモデルは、主にビットコインで採用されている状態管理のアプローチです。このモデルでは、ユーザーの「残高」は、特定のアドレスに紐づいた「未使用のトランザクション出力(Unspent Transaction Output)」の集合として表現されます。
これを理解するためには、まずUTXOモデルにおけるトランザクションの構造を見る必要があります。UTXOモデルのトランザクションは、一つ以上の「入力(Input)」と一つ以上の「出力(Output)」から構成されます。
- 入力(Input): これは、過去のトランザクションの未使用の出力を指します。つまり、あるトランザクションの入力は、過去に自分宛てに送られてきた、まだ次のトランザクションの入力として使われていない「コイン」の断片です。入力を使用するためには、その出力を受け取ったアドレスの秘密鍵による署名が必要です。
- 出力(Output): これは、新しい「未使用のトランザクション出力(UTXO)」を生成します。各出力には、送付先のスクリプト(通常はアドレスの公開鍵ハッシュ)と、送付する金額が紐づけられます。これらの新しい出力は、将来のトランザクションの入力として使用される可能性があります。
例えば、AさんがBさんに10 BTCを送金する場合を考えます。Aさんは過去にいくつかのトランザクションで合計15 BTCを受け取っており、それらが複数のUTXOとして存在しているとします(例:3 BTCのUTXO、5 BTCのUTXO、7 BTCのUTXO)。Aさんは、Bさんに10 BTCを送るために、これらのUTXOの一部または全部を入力として使用します。仮に5 BTCのUTXOと7 BTCのUTXO(合計12 BTC)を入力として選択した場合、トランザクションの出力は以下のようになります。
- 出力1:Bさんのアドレス宛てに10 BTC
- 出力2:Aさん自身のアドレス宛てに2 BTC(お釣り)
このトランザクションが承認されブロックに取り込まれると、入力として使用された5 BTCと7 BTCのUTXOは「使用済み(Spent)」となり、出力として生成されたBさん宛ての10 BTCとAさん宛ての2 BTCが新しいUTXOとして誕生します。Aさんの「残高」は、Aさんのアドレスに関連付けられたまだ使用されていないUTXOの合計値として計算されます。
コード例(概念的な疑似コード):
class UTXO:
transaction_id: str # このUTXOが生成されたトランザクションのID
output_index: int # そのトランザクションの何番目の出力か
value: int # 金額
script_pub_key: str # ロック条件(通常は受取人の公開鍵ハッシュ)
class TransactionInput:
previous_output: UTXO # 使用する過去のUTXOへの参照
signature: str # 秘密鍵による署名
script_sig: str # アンロック条件を満たすためのデータ(通常は署名)
class TransactionOutput:
value: int # 送金額
script_pub_key: str # ロック条件(通常は受取人の公開鍵ハッシュ)
class Transaction:
inputs: list[TransactionInput]
outputs: list[TransactionOutput]
# 例:Aさんのウォレットが持つUTXOリスト
# wallet_utxos = [utxo1(value=5), utxo2(value=7)]
# 新しいトランザクションの作成 (Bさんに10を送る)
# new_tx = Transaction()
# new_tx.inputs.append(TransactionInput(previous_output=utxo1, signature=signed_data_from_A_private_key))
# new_tx.inputs.append(TransactionInput(previous_output=utxo2, signature=signed_data_from_A_private_key))
# new_tx.outputs.append(TransactionOutput(value=10, script_pub_key=script_for_B_address)) # Bさんへ
# new_tx.outputs.append(TransactionOutput(value=2, script_pub_key=script_for_A_address)) # Aさんへ (お釣り)
UTXOモデルは、個々のお金の断片(UTXO)の移動を追跡するイメージであり、会計帳簿で個々の取引を記録し、最終的な残高を計算するアプローチに似ています。
UTXOモデルの主な特徴:
- 高いプライバシー: トランザクションごとに新しいお釣り用のアドレスを生成することが推奨されるため、アドレスの再利用を減らし、取引履歴の追跡を困難にする効果があります。
- 並列処理の容易さ: 異なるUTXOセットを使用するトランザクションは相互に干渉しないため、並列での検証や処理が比較的容易です。
- シンプルで明確なトランザクション: 各トランザクションは、どのUTXOを消費し、どのUTXOを生成したかが明確です。
- スクリプトによる柔軟性: Outputの
script_pub_key
やInputのscript_sig
に特定のスクリプト言語を使用することで、マルチシグ(複数署名)などの複雑な支払い条件を実現できます。
UTXOモデルの課題:
- 残高計算の複雑さ: 特定のアドレスの現在の残高を知るためには、そのアドレス宛てのすべてのUTXOを集計する必要があります。
- スマートコントラクトとの相性: シンプルな送金には向いていますが、イーサリアムのような複雑なスマートコントラクトの状態遷移をUTXOモデルで管理するのは困難です(ただし、UTXOベースでもスマートコントラクト機能を拡張する試みは存在します)。
アカウントモデルの仕組み
アカウントモデルは、イーサリアムをはじめとする多くのブロックチェーンで採用されています。このモデルは、従来の銀行システムや多くのデータベースシステムにおける「口座」や「レコード」に近い考え方に基づいています。
アカウントモデルでは、各ユーザーやスマートコントラクトは固有の「アカウント」を持ち、そのアカウントには現在の「状態(State)」が紐づけられています。基本的なアカウントの状態には、以下のような情報が含まれます。
- Nonce: そのアカウントから送信されたトランザクションの連番。トランザクションの順序性を保証し、二重支払いを防ぎます。
- Balance: そのアカウントが保持しているデジタル資産(例:Ether)の量。
- StorageRoot: スマートコントラクトのアカウントの場合、そのコントラクトに関連するデータ(ストレージ)のルートハッシュ。
- CodeHash: スマートコントラクトのアカウントの場合、そのコントラクトのコードのハッシュ。
トランザクションが発生すると、システム全体の状態(State)が更新されます。例えば、AさんのアカウントからBさんのアカウントへ10 ETHを送金するトランザクションは、以下のような状態遷移を引き起こします。
- 送信元AさんのアカウントのNonceがインクリメントされます。
- 送信元AさんのアカウントのBalanceから10 ETHが減算されます。
- 宛先BさんのアカウントのBalanceに10 ETHが加算されます。
このトランザクションが承認されると、ブロックチェーンの次のブロックには、これらの状態変更が反映された新しいグローバルな状態のハッシュが記録されます。ブロックチェーン全体は、一連のトランザクションによって状態が遷移していく巨大なステートマシンとして見なすことができます。
コード例(概念的な疑似コード):
class AccountState:
nonce: int
balance: int
storage_root: str # スマートコントラクト用
code_hash: str # スマートコントラクト用
# グローバルな状態 (アドレスとそのアカウント状態のマッピング)
# global_state = {
# "address_A": AccountState(nonce=10, balance=100),
# "address_B": AccountState(nonce=5, balance=50),
# "contract_C": AccountState(...)
# }
class Transaction:
from_address: str
to_address: str
value: int
data: str # スマートコントラクト呼び出し用のデータ
nonce: int
gas_limit: int
gas_price: int
signature: str
# 例:AさんからBさんに10を送るトランザクション処理(簡略化)
# tx = Transaction(from_address="address_A", to_address="address_B", value=10, ...)
# トランザクション処理関数 (状態を更新)
# def process_transaction(tx: Transaction, current_state: dict[str, AccountState]) -> dict[str, AccountState]:
# sender_account = current_state[tx.from_address]
# recipient_account = current_state[tx.to_address]
# if sender_account.nonce != tx.nonce:
# raise Exception("Nonce mismatch")
# if sender_account.balance < tx.value + tx.fee: # 手数料考慮
# raise Exception("Insufficient balance")
# # 状態更新
# sender_account.nonce += 1
# sender_account.balance -= tx.value + tx.fee
# recipient_account.balance += tx.value
# # スマートコントラクト呼び出しなどの処理があればここに追加
# return current_state # 更新された状態を返す
アカウントモデルは、各アカウントの現在の状態(特に残高)を直接参照できるため、残高管理が直感的で容易です。これは従来の銀行口座のイメージに非常に近いです。
アカウントモデルの主な特徴:
- シンプルで直感的な残高管理: 各アカウントの現在の残高はStateとして直接保持されているため、容易に参照できます。
- スマートコントラクトとの親和性: スマートコントラクトの複雑な状態遷移や関数呼び出しは、アカウントの状態(Balance, Storage, Codeなど)を変更するトランザクションとして自然にモデル化できます。
- 効率的なストレージ: UTXOモデルのように多数の小さなUTXOを管理する必要がなく、アカウントの状態を効率的に表現できます。
アカウントモデルの課題:
- プライバシー: 各アカウントの入出金履歴がそのアカウントに紐づいて記録されるため、UTXOモデルと比較すると追跡が容易になる傾向があります(ただし、プライバシーを強化する技術も開発されています)。
- 並列処理の制限: 同じアカウントの状態を変更する複数のトランザクションは、Stateの競合が発生するため、並列処理が難しくなります。Nonceによる順序付けも必要です。
- 二重支払いのリスク: Nonceの実装が不適切である場合や、Stateの同期に問題がある場合に、二重支払いのリスクが発生する可能性があります。
UTXOモデルとアカウントモデルの比較まとめ
| 特徴 | UTXOモデル | アカウントモデル | | :--------------- | :-------------------------------------------- | :------------------------------------------------ | | 残高管理 | 未使用UTXOの合計として計算 | アカウントの状態(State)として直接保持 | | トランザクション | 過去のUTXOを消費し、新しいUTXOを生成 | アカウントState間の状態遷移を引き起こす | | 構造 | 入力(参照元UTXO)と出力(新規UTXO)のリスト | 送信元、宛先、値、Nonce、データなどのフィールド | | プライバシー | 高い傾向(新しいお釣りアドレスの使用) | 低い傾向(アカウント履歴が追跡されやすい) | | 並列処理 | 比較的容易(異なるUTXOセットを使用する場合) | 困難(同じアカウントStateへのアクセス) | | スマートCとの相性| 限定的(拡張は試みられている) | 高い(State遷移として自然にモデル化できる) | | 主要な採用例 | Bitcoin, Litecoin, Zcashなど | Ethereum, Polkadot, Cosmosなど |
どちらのモデルを選ぶか?
UTXOモデルは、主に単純な価値の移転(ペイメント)に特化したブロックチェーンに適しています。ビットコインが通貨としての機能を重視していることからも、このモデルが選択された理由が理解できます。未使用のUTXOを効率的に管理し、高いセキュリティと比較的良好なプライバシー特性を提供します。
一方、アカウントモデルは、複雑な状態遷移を伴うスマートコントラクトの実行に非常に適しています。イーサリアムが分散型アプリケーション(DApps)のプラットフォームとして設計されていることからも、その選択の理由が明確です。アカウントの状態を直接操作することで、スマートコントラクトのロジックをシンプルに表現しやすくなります。
どちらのモデルが優れているという単純な話ではなく、それぞれのモデルが目指すブロックチェーンの用途や特性によって選択されます。
まとめと次のステップ
本記事では、ブロックチェーンにおける二つの主要な取引モデル、UTXOモデルとアカウントモデルについて解説しました。UTXOモデルが未使用のトランザクション出力を追跡する会計帳簿のようなアプローチであるのに対し、アカウントモデルはアカウントごとの残高を直接管理する銀行口座のようなアプローチを取ります。
これらのモデルは、ブロックチェーンの設計思想、特にトランザクションの処理方法、状態管理、スケーラビリティ、プライバシー特性に大きな影響を与えます。Webエンジニアの皆さんにとっては、従来のデータベースシステムとの違いを理解し、ブロックチェーンがどのようにして分散環境下で一貫した状態を維持しているのかを深く考察する良い機会になったのではないでしょうか。
これらのモデルの理解は、具体的なブロックチェーンプロトコル(BitcoinやEthereumなど)の内部実装を学ぶ上での強固な基礎となります。さらに学習を進める際は、各プロトコルがどのようにこれらのモデルを実装しているのか、トランザクションの署名や検証プロセスがモデルによってどう異なるのか、といった点に注目すると良いでしょう。また、最近ではこれらのモデルのハイブリッド型や、それぞれの欠点を克服するための新しいアプローチも研究されていますので、興味があれば調べてみるのも良いでしょう。