OP_CTV
概要
OP_CHECKTEMPLATEVERIFY(OP_CTV)は、OP_NOP4に登録提案中のオペコード
UTXOのロックを解く際に送金先が予め設定していたものになっていないと失敗するオペコード
つまり、予め送金先の指定が可能になる。
ソフトフォークでの導入予定
OP_CTVの評価方法
DefaultCheckTemplateVerifyHashと呼ばれるハッシュ値は以下のもので算出
バージョン
locktime
scriptsig hash
input数
シーケンスハッシュ
output数
output ハッシュ
input index
これらの要素をハッシュ化?したものをアウトプットに入れておく
使用する際は予め決めておいた上記の内容でトランザクションを作成する
OP_CTVが呼ばれたタイミングでスタックしてあるコミットメントとトランザクションの中身を比較し、同じものであれば通す
用途
鍵を持っていなくても使用用途を制限できるという点で今までのオペコードとは違う
スマコン的な使い方が可能になるはず
取引所など複数人がいて誰かが裏切ると資金を抜き取られるかもしれない場面において、予め使用方法が決められていると奪われるリスクが減らせる。コールドウォレットからホットウォレット移動時など。
実装予定の評価部分のコード
code:execute_bip_119(self):
def execute_bip_119(self):
# ソフトフォークの起動前 / 起動に失敗したとき
if not self.flags.script_verify_default_check_template_verify_hash.If not self.flags.script_verify_default_check_template_verify_hash:
# 早まった利用を避けるために、ノードローカルなポリシーで設定する可能性がある
if self.flags.script_verify_discourage_upgradable_nops:
return self.errors_with(errors.script_err_discourage_upgradable_nops)
return self.return_as_nop()
# CTVは常に少なくとも1つのスタック引数を必要とする
if len(self.stack) < 1:
return self.errors_with(errors.script_err_invalid_stack_operation)
# CTVは32バイトの引数に対してのみハッシュを検証する
if len(self.stack-1) == 32: # DoS対策に必要な事前計算されたデータが利用可能であることを確認。
# あるいは初回使用時にキャッシュする
if self.context.precomputed_ctv_data == None:
self.context.precomputed_ctv_data = self.context.tx.get_default_check_template_precomputed_data()
if stack-1 != self.context.tx.get_default_check_template_hash(self.context.nIn, self.context.precomputed_ctv_data) return self.errors_with(errors.script_err_template_mismatch)
return self.return_as_nop()
# 将来のアップグレードで、このオペコードに異なる長さの引数に対するセマンティクスが追加される可能性があります。
# なので、該当する場合は使用を控える
if self.flags.script_verify_discourage_upgradable_nops.If self.flags.script_verify_discourage_upgradable_nops:
return self.errors_with(errors.script_err_discourage_upgradable_nops)
それ以外の場合
return self.return_as_nop()
code:get_default_check_template_precomputed_data(self):
def get_default_check_template_precomputed_data(self):
result = {}
# scriptSigがなければハッシュを事前計算する必要はない
if any(inp.scriptSig for inp in self.vin):
result"scriptSigs" = sha256(b"".join(ser_string(inp.scriptSig) for inp in self.vin)) # 同じ値を事前計算する。 # 同じ値がBIP-341でも事前に計算され定義されており、共有することができる
result"sequences" = sha256(b"".join(struct.pack("<I", inp.nSequence" for inp in self.vin))) # 同じ値がBIP-341でも事前に計算されて定義されており、共有することができる
result"outputs" = sha256(b"".join(out.serialize() for out in self.vout))) return result
code:get_default_check_template_hash(self, nIn, precomputed = None)
def get_default_check_template_hash(self, nIn, precomputed = None):
if precomputed == None:
precomputed == None: precomputed = self.get_default_check_template_precomputed_data()
r = b""
# 4バイトの符号付き整数としてパックする
r += struct.pack("<i", self.nVersion)
# 4バイトの符号なし整数としてパックする
r += struct.pack("<I", self.nLockTime)
# ハッシュが存在しない場合は含めない。
# scriptSigs
if "scriptSigs" in precomputed:
# 4バイトの符号なし整数としてパックする。
r += struct.pack("<I", len(self.vin))
r += precomputed"sequences" # pack as 4 byte unsigned integer. # 4バイトの符号なし整数としてパックする
r += struct.pack("<I", len(self.vout))
r += precomputed"outputs" # 4 バイトの符号なし整数としてパックします. # 4バイトの符号なし整数としてパックする
r += struct.pack("<I", nIn)
return sha256(r)
デプロイ方法
デプロイは、Speedy Trialを通じてデプロイされたBIP 9 VersionBitsを介して行われる可能性があります。Bitcoin Coreリファレンス実装は、それがBitcoin Coreに実装された現在の活性化メカニズムであるため、Speedy Trialに一致するように構成された、以下のパラメータを含んでいます。他の方法がより広いBitcoinコミュニティによって支持されるようになれば、それが代わりに使用されるかもしれません。
実装の開始時間とビットは現在ビット5とNEVER_ACTIVE/NO_TIMEOUTに設定されていますが、これはBIPがドラフトである間、変更される可能性があります。
BIP-119 が ACTIVE 状態になり SCRIPT_VERIFY_DEFAULT_CHECK_TEMPLATE_VERIFY_HASH フラグが強制されるまで、ノード実装はポリシーに対して SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS として(メンプールへのエントリーを拒否して)NOP4を実行するべき(推奨)で、(ブロック検証中に)合意に対して NOP として評価しなければなりません。
CHECKTEMPLATEVERIFYの使用を容易にするために、scriptSigデータのないPayToBareDefaultCheckTemplateVerifyHashという共通のケースを、中継を許可するために標準化してもよい(することが推奨される)。将来のテンプレートタイプは、実装者の好みに応じて、ポリシーの変更に伴って後に標準化されるかもしれない。
Relational
理由
CHECKTEMPLATEVERIFYの目標は、既存のコードベースへの影響を最小限にすることです -- 将来、より複雑だが安全であることが示されている使用例を認識するようになれば、新しいテンプレートタイプが追加される可能性があります。
以下では、ルールをひとつずつ説明します。
現在の入力インデックスにあるトランザクションのDefaultCheckTemplateVerifyHashはスタックの最上位に一致します。
コミットされるデータのセットは、入力以外のトランザクションのTXIDに影響を与える可能性のあるデータのスーパーセットである。これは、与えられた既知の入力に対して、TXIDも前もって知ることができることを保証する。さもなければ、CHECKTEMPLATEVERIFYは、チャネルがEltooのようなプロトコルを使用して構築されない限り、償還TXIDが改ざんされ、事前署名トランザクションが無効になり得るので、バッチチャネル作成構築に使用できないでしょう。したがって、TXIDを予測可能にすることで、CTVは任意のサブプロトコルとより適合しやすくなります。
バージョンとロックタイムへのコミット
これらの値をコミットしなければ、TXIDを変更するのと同様に、出力の消費を任意に遅延させることが可能である。
これらの値を特定の値に制限するのではなく、コミットすることで、CHECKTEMPLATEVERIFYのユーザーが好きなようにバージョンとロックタイムを設定できるようになり、より柔軟になります。
ScriptSigsハッシュへのコミット
P2SH の segwit トランザクションでない限り、segwit トランザクションの scriptsig は正確に空でなければなりません。P2SH は CHECKTEMPLATEVERIFY と互換性がありません(P2SH ハッシュが壊れていない限り)。なぜなら、テンプレートハッシュは ScriptSig にコミットしなければならず、それはハッシュサイクルである redeemscript を含んでいなければならないからです。
セグウィット入力を使用しない場合の不正を防止するために、スクリプトシグにもコミットします。これにより、レガシー出力に対する正確なscriptsigがコミットされている限り、レガシー事前署名付きspendで2入力CHECKTEMPLATEVERIFYを使用することが可能になります。これは、単にCHECKTEMPLATEVERIFYで設定されるscriptSigを禁止するよりも堅牢です。
トランザクション内でscriptSigが設定されない場合、データをハッシュしたり、DefaultCheckTemplateVerifyHashに含める目的はないため、それを除外する。segwitではscriptSigを空にすることが義務付けられているため、scriptSigが設定されないことが一般的だと思われる(不正確さを回避するため)。
SIGHASH_ALL署名を最適化するために、各トランザクションですでに事前計算されているため、値そのものではなくハッシュに対してコミットすることにしました。
ハッシュをコミットすることで、さらにスクリプトから DefaultCheckTemplateVerifyHash を安全かつ曖昧さなく構築することが簡単になります。
入力数へのコミット
トランザクションで複数の入力を使用することを許可すると、2つの出力が同じ出力セットへの支払いを要求することが可能になり、結果として意図した支払いの半分が破棄される「half-spend」問題が発生する。
さらに、どの入力を共同で使用できるかという制限は、安定したTXIDが要求されるペイメントチャネルの構造にとって重要である(更新はすべての入力の組み合わせで署名される必要がある)。
しかし、複数の入力を許可する正当なユースケースも存在する。たとえば、以下のようなものである。
スクリプトのパス。
パスA:<+24時間> OP_CHECKSEQUENCEVERIFY OP_CHECKTEMPLATEVERIFY <Payアリス1ビットコイン(1入力)nLockTime for +24 hours> 。
パスB:OP_CHECKTEMPLATEVERIFY <Pay Bob 2 Bitcoin (2 inputs)>。
この場合、2つ目の出力を加えて、Bobに2BTCを支払うまで、24時間ある。もし24時間が経過したら、アリスは契約から1BTCを償還することができる。両方の入力UTXOはまったく同じパスBを持つこともできるし、片方だけ持つこともできる。
これらの構成の問題は、入力の順序がN!あり、順序を制限することは一般に不可能であることである。
CHECKTEMPLATEVERIFYを使うと、ユーザは費やされる入力の正確な数を保証することができます。一般的に、複数の入力でCHECKTEMPLATEVERIFYを使用することは難しく、微妙な問題を露呈しますので、特定のアプリケーションを除いて複数の入力を使用するべきではありません。
原理的には、シーケンスハッシュ(下記)へのコミットは暗黙のうちに入力数をコミットし、このフィールドは厳密には冗長になります。しかし、この数を別途コミットしておくと、スクリプトから DefaultCheckTemplateVerifyHash を簡単に構築できるようになります。
ビットコインのコンセンサス解読ロジックでは、ベクターを MAX_SIZE=33554432 に制限しており、これは uint16_t より大きく、 uint32_t より小さいため、入力数は uint32_t として扱います。32ビットは、OP_CATが追加された場合、ビットコインの現在の数学のオペコードを使用して操作するのにも適しています。ブロック内の最大入力数はブロックサイズによってさらに制限され、約25,000となり、uint16_tに収まりますが、これは不要な抽象化リークです。
シーケンスハッシュへのコミット
シーケンスにコミットしない場合、TXIDを不正に変更することができます。これはまた、OP_CSVなしで相対シーケンスロックを強制することができます。OP_CSVはリテラル値ではなく、最小のnSequence値を強制するため、CHECKTEMPLATEVERIFYとOP_CSVのペアだけでは不十分です。
SIGHASH_ALL署名を最適化するために各トランザクションですでに事前計算されているため、値そのものではなく、ハッシュにコミットします。
ハッシュをコミットすることで、さらにスクリプトから安全かつ明確にDefaultCheckTemplateVerifyHashを構築することが簡単になります。
出力数へのコミット
原理的には、Outputs Hash (下記) にコミットすると、暗黙のうちに出力数をコミットすることになり、このフィールドは厳密には冗長となる。しかし、この数を別途コミットすることで、スクリプトからDefaultCheckTemplateVerifyHashを構築することが容易になる。
出力数は uint32_t として扱います。これは COutpoint インデックスが uint32_t であるためです。さらに、ビットコインのコンセンサスデコーディングロジックは、ベクターを MAX_SIZE=33554432 に制限しており、これは uint16_t より大きく、 uint32_t よりも小さいです。32 ビットは、ビットコインの現在の数学オペコードを使用して操作するのにも適しており、万が一 OP_CAT が追加された場合でも安心です。
出力ハッシュへのコミット
これは、UTXOを使うことで要求された正確なアウトプットを作成することが保証されることを意味します。
SIGHASH_ALL署名を最適化するために、各トランザクションですでに事前計算されているため、値そのものではなくハッシュに対してコミットしています。
ハッシュにコミットすることで、さらにスクリプトから安全かつ明確に DefaultCheckTemplateVerifyHash を構築することが容易になります。
現在の入力のインデックスへのコミット
現在実行中の入力のインデックスにコミットすることは、 マージャビリティ対策として厳密には必要ではありません。 しかし、入力の順序を制限することで、プロトコル設計者にとってのマージャビリティの 原因を排除することができます。
しかし、インデックスへのコミットは、half-spend問題に対する鍵の再利用の脆弱性を排除する。CHECKTEMPLATEVERIFYスクリプトは特定のインデックスで使用されることを約束するので、これらのスクリプトの再利用インスタンスは、同じインデックスで使用することはできません。これは、同一トランザクション内で使用できないことを意味する。これにより、半消費の脆弱性がないウォレットボールト・コントラクトをより安全に設計することができる。
現在のインデックスにコミットしても、複数のインデックスで使用できるCHECKTEMPLATEVERIFYを表現することは妨げられない。現在のスクリプトでは、CHECKTEMPLATEVERIFYオペレーションは、各インデックス(または将来的にはTapscriptブランチ)に対してOP_IFでラップすることが可能です。OP_CATまたはOP_SHA256STREAMがBitcoinに追加された場合、ハッシュ化前にインデックスが単にウィットネスによって渡されるかもしれません。
ハッシュによる値へのコミット
ハッシュによる値のコミットは、スクリプトから DefaultCheckTemplateVerifyHash を構築することをより簡単に、より効率的にします。設定されることを意図していないフィールドは、再ハッシュするための O(n) オーバーヘッドを発生させずにハッシュによってコミットされるかもしれません。
さらに、将来 OP_SHA256STREAM が追加された場合、スクリプトの中でハッシュにコミットすることで、O(n) オーバーヘッドを発生させずに単一の出力を出力リストに追加できるようなスクリプトを書くことができるようになるかもしれない。
SHA256の使用
SHA256はビットコインのセキュリティ基準を満たす32バイトのハッシュで、テンプレートプログラムをプログラム的に作成するためにBitcoin Script内部で既に利用可能になっています。
20バイトのハッシュであるRIPEMD160も、いくつかの文脈では有効なハッシュであり、いくつかの利点があります。手数料の効率性を考えると、RIPEMD160は12バイトを節約することができます。しかし、RIPEMD160は、第三者が作成したプログラムがトランザクションプリイメージのbirthday-attackの対象となる検証に関するリスクをもたらすため、BIP-119では選択されなかった。 非タグ付きハッシュの利用
Taproot/Schnorr BIPではTagged Has (SHA256(SHA256(tag)||SHA256(tag)||msg)) を使用し、Taproot leafs, branches, tweak, and signaturesがセキュリティ上の vulnerability をもたらすような形で重ならないように配慮しています。 OP_CHECKTEMPLATEVERIFYは、ハッシュが効果的に外部で、つまり、OP_CHECKTEMPLATEVERIFY自身によってタグ付けされ、したがって、他のハッシュと混同できないので、この種の脆弱性にさらされることはないです。
たとえ明白な利点やコストがなくても、タグ付けされたハッシュにすることは保守的な設計上の決定でしょう。しかし、将来的にOP_CATがビットコインに導入された場合、OP_CHECKTEMPLATEVERIFYハッシュを動的に構築するプログラムの容量効率が悪くなる。そのため、BIP-119ではタグなしの素のハッシュが使用されている。
設計上のトレードオフとリスク
コインが鋳造され、そのコインをどのように使うか、あるいは使わないか、あるいはメタデータを伝播することが要求されるかについて、永久的な制約を受ける可能性があるからです。
CHECKTEMPLATEVERIFYアプローチでは、コベナンツは単純なテンプレートに厳しく制限されています。CHECKTEMPLATEVERIFYテンプレートの構造は、構築時に出力が正確に分かっていなければならないようなものである。デストラクチャリング論に基づくと、有限のステップ数で展開するテンプレートしか作成できな い。したがって、テンプレート化されたトランザクションは、この点で、すべての入力を直接作成するトランザクションと理論上同じぐらい安全である。
さらに、テンプレートは既知の入力数としてのみ使用できるように制限されているため、意図せず「半消費」問題が発生することを防いでいる。
テンプレートは、そのように制限されているため、いくつかのリスクを抱えています。
サービス拒否とバリデーションコスト
CTVは、事前に計算されたハッシュをチェックするか、固定長の引数のハッシュを計算することにより、DoSを引き起こすことなく非常に安価に検証できるように設計されています(そのうちのいくつかはより高価な計算からキャッシュされるかもしれません)。
特に、CTVは、クライアントがすべてのscriptSigs、シーケンス、および出力上のハッシュの計算をキャッシュすることを必要とします。CTV以前は、scriptSigsのハッシュは要求されていなかった。CTVはまた、任意の非空scriptSigの存在がハッシュされることを必要とするが、これはscriptSigsハッシュの一部として処理されることができる。
このように、キャッシュが利用可能な場合、コンセンサス時にCTVハッシュを評価することは常にO(1)の計算となります。これらのキャッシュは、CHECKSIGの動作における同様の問題のために、通常利用可能でなければなりません。キャッシュの計算はO(T)(トランザクションのサイズ)である。
キャッシュなしで DoS 問題が発生する可能性のあるスクリプトの例を以下に示します。
<H> CTV CTV CTV... CTV