Elements/Liquidでコベナンツを作ってみた
ElementsとはLiquidなどのサイドチェーンを運用するためのソフトウェアです。Bitcoinを拡張した実装となっています。
Elementsに追加された新OPコード
Elementsでは以前からビットコインにはないOPコードがいくつかあります。OP_CATとOP_CHECKSIGFROMSTACKを使うことでコベナンツを実現できますが、この方法は計算コストがかかるというデメリットがあります。そこでインプットやアウトプットを直接調べるためのOPコードが追加されました(詳細はこちら)。これらのOPコードはSegwit V1(別名P2TRアドレス)として使うことができます。例えば、OP_INSPECTOUTPUTVALUEはアウトプットの金額を参照することができ、以下のようなスクリプトを構成することで送金金額を制限できます。こちらのツールを使うことで、スクリプトの実行経過を確認することができます。
//インデックス0のアウトプットの金額を取得
OP_0 OP_INSPECTOUTPUTVALUE OP_1 OP_EQUALVERIFY
//1000のリトルエンディアンを指定
<0xe803> OP_SCRIPTNUMTOLE64
//アウトプットの金額が1000以上かのチェック
OP_GREATERTHANOREQUAL64
以下は再帰的なコベナンツの例です。インプットとアウトプットのスクリプトが同じであることを強制させています。さらに上記の送金金額の制限も追加しています。
//インプットとアウトプットのスクリプトが等しいとする制約
OP_0
OP_INSPECTINPUTSCRIPTPUBKEY
OP_1
OP_EQUALVERIFY
OP_0
OP_INSPECTOUTPUTSCRIPTPUBKEY
OP_1
OP_EQUALVERIFY
OP_EQUAL
//以下はコベナンツへの金額を1000以上とする制約
OP_0
OP_INSPECTOUTPUTVALUE
OP_1
OP_EQUALVERIFY
<0xe803>
OP_SCRIPTNUMTOLE64
OP_GREATERTHANOREQUAL64
OP_VERIFY
これをアドレスへ変換すると以下のようになります。内部キーはなんでも構いませんが今回は以下を指定しています。このP2TRアドレスへ1000sat以上を送金したTXがこちらです。
P2TR address: tex1ph4cmjwxm9lf7hut5d6und0p7quyusjwdlnn2pjyk49v2lryq852s9yr2mg
internal key: 8ea27103fb646a2cea9eca9080737e0b23640caaaef2853416c9b286b353313e
このようなコベナンツを構成することで署名なし(スクリプトにOP_CHECKSIGを含めない)なスクリプトを作ることができます。今回はliquidjs-libを使って署名なしにコベナンツを消費するTXを作ってみます。
liquidjs-lib
ビットコインの部分的な署名はPSBTが有名ですが、ElementsではPSETと呼びます。liquidjs-libにもこのPSETクラスがあるのでこれを使って署名をしていきます。通常のP2TRアドレスの消費は、Signer.addSignatureで署名してFinalizerでファイナライズさせます。このデフォルトのFinalizerは署名検証が含まれるので、上記で紹介したような署名のないスクリプトを使うと以下のようなエラーになります。
`Missing partial signatures for input at index ${inIndex}. If the script does not have a CHECKSIG operation you must pass a custom finalizer function`
そこで以下のようなcustomFinalizerを作ってあげます。
const customFinalizer = (inIndex, pset) => {
const input = pset.inputs[inIndex];
if (input.isTaproot()) {
const stack = [input.tapLeafScript[0].script, input.tapLeafScript[0].controlBlock];
return {
finalScriptWitness: (0, utils_1.witnessStackToScriptWitness)(stack),
};
}
};
このcustomFinalizerを使えば署名なしのトランザクションを作ることができます。実際に作ったTXはこちら。ちゃんとインプットとアウトプットのスクリプトが同じかつ金額が1000以上となっていることが確認できます。このTXをさらに消費することも可能なので、興味のある方はチャレンジしてみくてください。
まとめ
Elementsの新しいイントロスペクト用OPコードを使うことで簡単にコベナンツを実装することができました。今回作った再帰的なコベナンツは不完全なものなので、こちらのサイトなどを参考にさらに堅牢なコベナンツを作れるようになりたいですね。