Cointelegraph Researchによるビットコイン・コベナンツに関する技術記事シリーズの第5回です。前回の記事はこちらからご覧ください。

MATT(Merkleize All The Things)は、ステートフルなコントラクトとスケーラブルな不正証明を可能にする、ビットコイン向けのスマートコントラクト・フレームワークです。ステートフルなコントラクトは、複数のトランザクションにわたって情報を引き継ぎ、コントラクトの進行に応じてその情報を更新できます。標準的なビットコインのスクリプトでは、各トランザクションが過去の実行内容を記憶せずに個別に評価されるため、この機能は提供されません。

Sapioのようなスクリプト言語(OP_CTVを基盤とする)に示されるように、あらゆるコベナント提案は、ある程度まで状態を保持するUTXOを実現できます。これは、アドレスを有限状態オートマトンとして表現し、トランザクションを状態遷移として機能させることで実現します。しかし、この方式では数値ロジックをエレガントに実装する方法がありません。この不足を補うのがMATT、特にOP_CCVを通じた実装案です。MATTでは、支出時にコミット済みデータを計算し、それを次の出力に再び埋め込めるため、あらかじめ定められた状態の集合から選ぶのではなく、各支出時に新しい状態を動的に導出できます。

OP_CCVが状態をTaprootキーにコミットする仕組み

MATTの提案を理解するには、まずTaprootが公開鍵を通じてデータをコミットする仕組みを確認しておくと役立ちます。すべてのTaproot出力には、内部キーと呼ばれるベース公開鍵が含まれています。この鍵は、暗号学的に変更、つまり「tweak」して追加情報をコミットできます。形式的には、tweak後の公開鍵は Q = P + t·G です。ここで、Pは内部キー、Gはsecp256k1楕円曲線の生成元、tはPとコミット対象データをタグ付きハッシュにした値です。

通常のTaproot出力では、このコミットメントは一般に、非公開の支出経路を含むtaptreeのMerkleルートを符号化します。MATTはこの仕組みを基盤として、コントラクトの状態もキーに埋め込みます。複雑なロジックをBitcoinのスクリプト言語に直接埋め込むのではなく、MATTは計算を一連の状態コミットメントとして符号化し、各状態遷移が有効であることだけをチェーン上で検証します。

MATTは、さまざまなオペコードの組み合わせで実現できます。最も直接的で仕様が明確な実装は、OP_CHECKCONTRACTVERIFY(OP_CCV)を利用します。OP_CCVは、Taproot内で状態を保持するUTXOを実現可能にする基本機能を提供します。OP_CCVを使うと、Taprootの入力キーまたは出力キーが、特定の状態データにコミットするようtweakされていることをスクリプトで検証できます。まず内部キーをコントラクトの現在の状態のハッシュでtweakし、その上にTaproot標準のtaptree用tweakを適用します。その結果得られる出力キーは、tapscript(支出条件の集合)とコントラクトの現在の状態の両方にコミットします。

続いてOP_CCVは、次の出力にどの状態を引き継ぐかを強制できます。スクリプトはwitnessスタックに与えられた状態データを調べ、それに対して計算を行い、更新後の状態が指定された出力に正しく再コミットされていることを要求できます。これにより、状態遷移がオンチェーンで強制されます。スタックの形式は次のとおりです。

<data> <index> <pk> <taptree> <mode> OP_CHECKCONTRACTVERIFY

このオペコードには、出力を検証する3つのモードと、別個の入力モードがあります。デフォルトモードでは、入力UTXOのBTC額全体が指定された出力に割り当てられます。deductモードでは、出力額と同じ額だけが割り当てられ、残額は追加の出力に利用できます。ignoreモードでは、金額に制約を設けずにスクリプトとデータのコミットメントを検証します。デフォルトモードとdeductモードは、検証対象の出力間で価値を保持するよう設計されているため、トランザクション手数料は別の手数料支払い用入力で支払う必要があります。専用の手数料入力とお釣りの出力をトランザクションに追加しても、コベナントの制約には抵触しません。これにより、OP_CTVやSIGHASH_ANYPREVOUTのような、より基本的なコベナント実装で起こり得る、資金の意図しない焼却を防げます。また、署名済みトランザクション方式のコベナントが抱える大きな制約にも対処できます。この方式では通常、金額を事前に定義するため、手数料の動的な調整や一部だけのアンボルトに対応できません。

OP_CCV + OP_CAT:Merkle証明と不正証明

OP_CCVをOP_CATと組み合わせると、MATTのフレームワークはさらに表現力を高めます。OP_CATはScript内でバイト列を連結できるため、オンチェーンでMerkle証明を構築し、検証できます。この構成では、Taproot出力に2種類のMerkleツリーが関連付けられます。1つ目はおなじみのTaprootスクリプトツリー(taptree)で、コントラクトの支出スクリプトをリーフに持ちます。2つ目は独立した状態ツリーで、計算の中間ステップなど、コントラクトのデータをリーフに持ちます。OP_CCV + OP_CATのスクリプトは、現在の状態ツリーのルートに対するMerkle証明を検証し、遷移後の新しいルートを計算したうえで、OP_CCVを使って次の出力がそのルートにコミットするよう強制できます。

この構成により、不正証明システムとスラッシング条件を実現できます。長大な計算はオフチェーンでコミットしておき、争われた特定のステップと、その二分木上の経路だけをオンチェーンで開示・検証すればよくなります。他のブロックチェーンシステムでは、楽観的ロールアップとして同様の仕組みが存在します。計算全体はオフチェーンで実行され、誰かが異議を申し立てた場合にのみ、オンチェーンのコントラクトが介入します。不正証明を可能にするには、計算を二分回路、またはMerkleツリーにコミットできる同等の段階的な表現にコンパイルする必要があります。OP_CCV + OP_CATを使えば、状態の一貫性を強制するためにBitVMが依存するLamportワンタイム署名や署名済みトランザクショングラフが不要になります。

不正証明にとどまらず、OP_CCVとOP_CATの組み合わせはボールトにも影響を及ぼします。OP_CCV単独でもボールトを実装できますが、OP_CATと組み合わせれば、OP_VAULTが提案する機能と実質的に同等の、フル機能のボールトを実現できます。OP_VAULTの作者であるJames O’Beirneが指摘しているように、OP_CCVはより汎用的で堅牢な設計であり、彼の以前の提案を実質的に置き換えるものです。

このシリーズの次の記事では、汎用的なコベナント提案から、OP_VAULT、OP_UNVAULT、OP_EVICTといった用途特化型オペコードの解説に移ります。これらのオペコードはそれぞれ、幅広いプログラマビリティを提供するのではなく、特定の問題を解決するために設計されています。

マガジン:ビットコイン保有者にeCashが届きました — 何に使えますか?