BIP: 345
Layer: Consensus (soft fork)
Title: OP_VAULT
Author: James O'Beirne <[email protected]>
Greg Sanders <[email protected]>
Anthony Towns <[email protected]>
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0345
Status: Draft
Type: Standards Track
Created: 2023-02-03
License: BSD-3-Clause
Post-History: 2023-01-09: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-January/021318.html [bitcoin-dev] OP_VAULT announcment
2023-03-01: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-March/021510.html [bitcoin-dev] BIP for OP_VAULT
このBIPは、特殊なコベナンツのサポートのためにコンセンサスサポートを追加する2つの新しいTapscript opcode、
OP_VAULT
とOP_VAULT_RECOVER
を提案する。
これらのopcodeをOP_CHECKTEMPLATEVERIFY
(BIP-119)と組み合わせることで、
ユーザーは事前に指定されたリカバリーパスを除き、指定されたコインが任意の宛先に使用される前に遅延時間を強制することができる。
この文書のライセンスは3条項BSDライセンス
Bitcoinを保管する際の危険性はよく知られている。Bitcoinのユーザーは、秘密鍵を保護するために多大な努力を払う必要があり、 一度プロビジョニングされた保管システムが、進化し続ける継続的な脅威に屈しないことを望む。 侵害が検出された場合、ユーザーには介入する手段がほとんどない。この提案では、 鍵の侵害による最悪の結果であるコインの損失を大幅に軽減する仕組みが導入されている。
予期せぬ支払いに介入する方法を導入することで、ユーザーは安全性の高い鍵の保管方法や、 最悪の場合にしか行使されないような(それ以外の場合だと法外な)、 通常とは異なるフォールバック戦略を取り入れることができる。この提案の目標は、 この戦略を最小限の複雑さであらゆる規模の管理者に使用できるようにすること。
個人がBitcoinを管理する場合の一般的な構成は、ハードウェアウォレットを使用した、シングルシグとパスフレーズになる。 このような構成を使用するユーザーは、ハードウェアへの物理的なアクセスだけでなく、 鍵の管理も単一のメーカーに依存することに伴うリスクを懸念する可能性がある。
このような個人は、OP_VAULT
を使用して、可能性の低いリカバリーパスとして非常に安全な鍵を利用すると同時に、
例えば使用する際に1日の遅延時間を設定した引き出し用のトリガー鍵で既存の署名手順を実行できるようにする。
リカバリーパスの鍵は性質上、非常に安全なものである必要があるため、日常の使用には向かない可能性がある。例えば、 鍵は何らかのアナログ方式で生成されることもあれば、(その後破棄される)古いコンピューターで生成され、 秘密鍵は紙の形式でのみ複製される。もしくは、鍵は異なるメーカーのデバイスを使用した2-of-3のマルチシグである可能性もある。 おそらく鍵は地理的または社会的に分散されている。
任意のBitcoinスクリプトポリシーを使用できるため、リカバリー鍵には多くの使用条件を含めることができる。たとえば、 安全性の高い鍵の安全性が高くなりすぎた場合に備えて、時間遅延のより「より簡単な」リカバリー方法も取れる。
ユーザーはモバイルデバイス上で、VaultのOutpointの使用をブロックチェーンで監視するソフトウェアを実行できる。 もしVaultに預けたコインが、予期しない方法で移動された場合、ユーザーはすぐにリカバリーパスにそれらを引き上げることができるが、 日常的にコインを使用することは(使用のための遅延を除けば)保管前と同様に機能する。
Bitcoinの機関管理者も同様の方法でVaultを使用する可能性がある。
この提案は$5レンチ攻撃を緩和するものだ。 使用可能になるまでの遅延を一週間に設定し、より長い相対的タイムロックを強制するスクリプトをリカバリーパスとして使用することで、 Vaultの所有者はそのコインにすぐにアクセスできないことを証明できる。著者の知る限り、 タイムロックされたコインを永続的にローリングしたり、いsんライできるサードバーティに依存したりせずに、 この防御を構成するにはこれが唯一の方法だ。
BitconのVaultについては、2016年(MES16)から正式に議論され、 2014年から 非公式に議論されてきた。予期しない使用を考慮して、遅延時間を設定してリカバリー機能をもたせることの価値は広く認識されている。
既存のコンセンサスルールでVaultを実装する唯一の方法は、大規模なマルチシグの構成で Vaultをエミュレートすることを除けば、使い捨ての鍵で作成された署名済みトランザクションを使用する方法。 この方法は2020年に初めて実証された。
残念ながら、このアプローチには、いくつかの欠点がある:
- Vaultコベナンツをエミュレートするために使用される一時鍵の生成と安全な削除が必要
- 金額と引き出しパターンを事前にコミットする必要がある。
- 資金が最終的な引き出し対象に向かう途中で通過するアドレスを事前にコミットする必要がある。これはおそらくUnvaultのタイミングにのみ知られるもの。
- 特定の手数料管理手法またはウォレットは、Vaultの作成時に決定する必要がある。
- Vaultアドレスが再利用されると、コインの損失が発生する。
- Vaultのbearer assetを表すトランザクションデータは永久に保存する必要があり、そうでないと資金を失う。
- 新しい残高がデポジットされる度にVaultの作成セレモニーを実行する必要がある。
OP_CHECKTEMPLATEVERIFYや SIGHASH_ANYPREVOUT などの 事前計算方のコベナンツの仕組みを導入すると、コベナンツがオンチェーンで強制されるため、 一時鍵を使用する必要がなくなり、必要なトランザクションはコンパクトなパラメーターのセットから生成できるため、 機密データ保管の負担が軽減される。このアプローチは2022年に初めて実証された。
ただし、事前計算は依然として必要で、金額や宛先、手数料の管理はすべて固定されている。 資金は固定された仲介者を通じて最終的な宛先まで送られる。 手数料の急騰や短期間の支払い遅延によるリカバリーを成功させるために不可欠なバッチ操作は実行できない。
[[File:bip-0345/withdrawal-comparison.drawio.png|frame|center]]
任意のトランザクションステートマシンをエンコードできる一般的なコベナンツの仕組みがあれば、 これらの問題を解決できるようになるが、その代償として、 おそらくブロックチェーン内で何度も複製されることになる複雑で大規模なスクリプトが必要になる。 このような一般的な枠組みの具体的な設計や展開のスケージュールは不確実だ。 このアプローチはは2016年に実証された。
この提案は、特殊なコベナンツを使用してトランザクション及び運用上のオーバーヘッドを最小限に抑えた遅延時間/リカバリーパスを提供することで、 上述した問題に対処することを目的としている。
この提案の設計目標は:
- 既存のVault構成の効率的な再利用。単一のVault構成は、同じscriptPubkeyかどうかに関係なく、複数のデポジットを受け取ることができる必要がある。
- 引き出しとリカバリーのバッチ操作により、複数のVaultコインを効率的に管理できる。
- 無制限の部分引き出し。これによりユーザーは新しいVaultのセットアップ手順を実行することなくVaultの残高の一部を引き出すことができる。
- 動的なUnvaultのターゲット。Vaultの引き出し先をVaultの作成時ではなく引き出し時に指定できるようにする。
- 動的な手数料管理。動的なターゲットと同様に、手数料率とソースの指定をVaultの作成時ではなくUnvault時に延期する。
これらの目標には、基本的な安全性に関する考慮事項(mempoolのPinnningに対して脆弱でないことなど)と、 作成されるアウトプットの数とスクリプトのサイズの両方の点で簡潔であることが求められる。
この提案は、将来導入される可能性のあるSIGHASHモード(たとえばSIGHASH_GROUP
など)または
手数料管理戦略(トランザクションスポンサーなど)と互換性があるように設計されている。
これらのopcodeを使用するとv3トランザクションリレーとエフェメラルアンカーの恩恵を受けることができるが、
厳密に依存するわけではない。
通常の使用では、Vaultは少なくとも2つのリーフを含むTaptreeに
コインを配置することでVaultが作成される。1つは、期待される引き出しプロセスを容易にするOP_VAULT
を含むスクリプトを使用するもの、
もう1つのOP_VAULT_RECOVER
が含まれるリーフは、引き出しが完了する前にいつでもコインを回収できることを保証する。
OP_VAULT
のルールは引き出しトランザクションでOP_VAULT
Tapleafを事前指定されたスクリプトテンプレートに置き換えることを許可し、
一部のパラメーターを引き出し(トリガー)時に設定できるようにすることで、タイムロックされた中断可能な引き出しを保証する。
宛先のアウトプット内のTaptree内の他のすべてのリーフは、変更されてはならない。
これによりリカバリーパスおよび元々Vaultに含まれていたその他の使用条件が維持される。
これは、2021年に提案されたTAPLEAF_UPDATE_VERIFY
の設計に似ている。
これらのTapleafの置換ルールは、以下で詳しく説明するが、タイムロックされた引き出しを保証する。
タイムロックは元のOP_VAULT
をパラメータによって固定され、
引き出しプロセスがトリガーされた際に選択されるアウトプットのセットに固定される(OP_CHECKTEMPLATEVERIFY
によって)。
この提案では、OP_CHECKTEMPLATEVERIFY
が提案された引き出しを最終的なアウトプットの特定のセットにバインドするために推奨方法として使用されているが、
OP_VAULT
は、他の種類の引き出しプロセスを容易にするため、他の(および将来の)opcodeと組み合わせ可能だ。
Vaultにはいくつかのステージがあり、そのいくつかはオプション:
- Vualtトランザクション:少なくとも1つのOP_VAULTリーフとOP_VAULT_RECOVERリーフを含むTaprootの構成にコインを入れ込む。
- トリガートランザクション:1つ以上のOP_VAULT Tapleafインプットをアウトプットに費やす。 このアウトプットはトリガー時に選択された固定のアウトプットセットへの引き出しでタイムロックされている。 これにより、特定のアウトプットのセットへの引き出しの意図が公的にブロードキャストされる。 トリガートランザクションは、Vault残高の一部を部分的なRevaultに割り当てるための追加のアウトプットが含まれる可能性がある。 これは金額の内Revaultされた部分を、使用しようとしているOP_VAULTをを含むインプットと同じscriptPubkeyに単純に埋め込むだけ。
- 引き出しトランザクション:トリガートランザクションが支払い遅延まで達した後、 タイムロックされ宛先ロックされたトリガーインプットを最終引き出しアウトプットと互換性のあるセット (たとえば、CHECKTEMPLATEVERIFYのハッシュ毎に)に使用する。
- リカバリートランザクション:OP_VAULT_RECOVER Tapleafを使用して1つ上のVaultインプットを 事前に指定されたリカバリーパスに支払う。これは引き出しトランザクションが承認される前の任意の時点で実行できる。 各インプットは、オプションで指定されたリカバリー認可スクリプト (OP_VAULT_RECOVERフラグメントの前に付加されるオプションのスクリプト)を満たすwitnessを必要とすることができる。 リカバリーの認可の使用には、後述する特定のトレードオフがある。
この提案の主な考慮事項は手数料管理がどのように扱われるか。動的な手数料管理を提供することはVaultの運用にとって非常に重要だ:
- 事前に計算された手数料より高額な手数料環境ではトランザクションが承認できなくなる傾向がある。
- 事前に指定された手数料ウォレットが、使用前に侵害されたり紛失したりする可能性がある。
しかし、動的な手数料管理によりPinnningベクトルが発生する可能性がある。 この提案が導入する新しい宛先ベースの支払いポリシーを使用する際にいは、これらのベクトルが不必要に導入されないように注意が払われている。
元々、この提案は、エフェメラルアンカーを含む改革されたトランザクションv3ポリシーに大きく依存していたが、 その後、これらのポリシーの変更や他の潜在的な手数料管理の仕組みの恩恵を受けるために改定された。
Tapscriptのopcode OP_SUCCESS187
(0xbb
)とOP_SUCCESS188
(0xbc
)がそれぞれOP_VAULT
および
OP_VAULT_RECOVER
を実装するための新しいルールで制約される。
OP_VAULT
を評価する際、スタックの予期される形式は上から下に以下のようになる。
<leaf-update-script-body>
<push-count>
[ <push-count> leaf-update script data items ... ]
<trigger-vout-idx>
<revault-vout-idx>
<revault-amount>
ここで、
<leaf-update-script-body>
は、シリアライズされたスクリプトの最小限のデータプッシュ。このスクリプトはleaf-update data items(リーフ更新データ項目)と連携して、現在実行中のものを置き換えるTaptreeアウトプット内のTapreafスクリプトを指示する。それ以外の場合、スクリプトの実行は失敗してすぐに終了しなければならない。<push-count>
は、スタックからポップするリーフ更新スクリプトの項目を示す、最小エンコードされた最大4バイトのCScriptNum。- この値が有効なCScriptNumにデコードできない場合、スクリプトの実行は失敗し、直ちに終了しなければならない。
- この値が0未満の場合、スクリプトの実行は失敗し、ただちに終了しなければならない。
- スタック上の
<push-count>
項目に続く項目が3つ未満の場合、スクリプトの実行は失敗し、直ちに終了しなければならない。つまり、<leaf-update-script-body>
をポップした後、すくなくとも3 + <push-count>
個の項目がスタックに残っている必要がある。
- 次の
<push-count>
スタック項目がスタックからポップされ、最小限にエンコードされたのプッシュデータ引数としてその先頭に置かれ、期待されるTapleaf置換スクリプトを構築する。プレフィックスとなるのがデータプッシュのみなのは、opcodeとしてOP_SUCCESSのようなものが付与され必要な検証をスキップするといったことをできなくするため。 <trigger-vout-idx>
は最小限にエンコードされた最大4バイトのCScriptNumで、オプションのrevaultアウトプットと組み合わせて、このインプットの資金を引き継ぎ、現在実行中のリーフとは別に同一のTaptreeを持つアウトプットのインデックス。- この値が有効なCScriptNumでない場合、スクリプトの実行は失敗し、直ちに終了しなければならない。
- この値が0未満か、アウトプットの数以上である場合、スクリプトの実行は失敗し、直ちに終了しなければならない。
<revault-vout-idx>
は、最小限にエンコードされた最大4バイトのCScriptNumで、トリガーアウトプットと組み合わせてこのインプットの資金を引き継ぎ、現在のインプットと同じscriptPubkeyを持つアウトプットのインデックスをオプションで指示する。- この値が有効なCScriptNumでない場合、スクリプトの実行は失敗し、直ちに終了しなければならない。
- この値が0未満か、アウトプットの数以上である場合、スクリプトの実行は失敗し、直ちに終了しなければならない。
- この値がマイナスで-1ではない場合、スクリプトの実行は失敗し、直ちに終了しなければならない。なぜ-1を許可するのか?負のインデックスは、Revaultアウトプットが存在しないことを示す。この値に任意の負の数値を許可すると、トランザクションが承認を待っている間にwitnessが細工可能(および肥大化)になる可能性がある。
<revault-amount>
は、最小限にエンコードされた最大7バイトのCScriptNumで、Revaultされるsatoshiの数を示す。- この値が有効なCScriptNumでない場合、スクリプトの実行は失敗し、直ちに終了しなければならない。
- この値が0以上でない場合、スクリプトの実行は失敗し、直ちに終了しなければならない。
- この値が非ゼロで、
<revault-vout-idx>
が負の場合、スクリプトの実行は失敗し、直ちに終了しなければならない。 - この値がゼロで、
<revault-vout-idx>
が-1でない場合、スクリプトの実行は失敗し、直ちに終了しなければならない。
スタックがパースされた後、以下の検証チェックが実行される。
- スクリプト毎のsigopsバジェットを60減らす(BIP-342参照)。 バジェットがゼロを下回った場合、スクリプトの実行は失敗し直ちに終了しなければならない。
<trigger-vout-idex>
で指定されたアウトプットをtriggerOutと呼ぶ。- triggerOutのscriptPubkeyがversion 1 witness programでない場合、スクリプトの実行は失敗し直ちに直ちに終了しなければならない。
<leaf-update-script-body>
を取得し、その先頭に<push-count>
個のleaf-updateスクリプトのデータ項目を最小限にエンコードされたデータプッシュを付加してスクリプトが構築され。これをleaf-update-scriptと呼ぶ。- triggerOutのscriptPubkeyが現在評価されているインプットのTaptreeのものと一致しない場合、スクリプトの実行は失敗し直ちに直ちに終了しなければならない。ただこの時、leaf scriptはleaf-update-scriptに置き換えられているものとする。
- 注:結果のTaprootアウトプットのパリティビットは変更できるための、新しいアウトプットの両方の値をチェックする必要がある。
<revault-vout-idx>
で指定されたアウトプット(インデックスの値が負でない場合)をrevaultOutと呼ぶ。- revaultOutのscriptPubkeyが使用されているscriptPubkeyと等しくない場合、スクリプトの実行は失敗し直ちに直ちに終了しなければならない。
- 実装に関する推奨事項:triggerOutとrevaultOut(存在する場合)の量の合計がこのインプットの値以上でない場合、
スクリプトの実行は失敗し直ちに直ちに終了しなければならない。これにより(少なくとも)このインプットVault金額が確実に引き継がれる。
- 金額のチェックは、最終的には遅延チェックで行われるが、このチェックは明らかに無効な支払いを回避するのに役立つ。
- このインプットの
nValue
から<revault-amount>
を引いた額が<trigger-vout-idx>
のアウトプットのnValue
に含まれていることを確認する遅延チェックをキューに入れる。 <revault-amount>
がゼロでない場合、<revault-vout-idx>
のアウトプットのnValue
に含まれていることを確認する遅延チェックをキューに入れる。- これらの遅延チェックは、以下の疑似コード(遅延チェック内)の観点から特徴づけることができる。
TriggerCheck(input_amount, <revault-amount>, <trigger-vout-idx>, <revault-vout-idx>)
- これらの遅延チェックは、以下の疑似コード(遅延チェック内)の観点から特徴づけることができる。
どの条件も失敗しない場合、単一のtrue値(0x01
)がスタックに残る。
OP_VAULT_RECOVER
(OP_SUCCESS188
、0xbb
)を評価する場合、スタックの予期される形式は次のようになる。
<recovery-sPK-hash>
<recovery-vout-idx>
ここで、
<recovery-sPK-hash>
は32バイトのデータプッシュ- これが32バイトでない場合、スクリプトの実行は失敗し直ちに直ちに終了しなければならない。
<recovery-vout-idx>
は、最大4バイトの最小限にエンコードされたCScriptNum
で、リカバリーアウトプットのインデックスを示す。- この値が有効なCScriptNumとしてデコードできない場合、スクリプトの実行は失敗し直ちに直ちに終了しなければならない。
- この値が0未満か、アウトプットの数以上の場合、
スタックがパースされると、以下の検証チェックが実行される:
<recovery-vout-idx>
のインデックスのアウトプットをrecoveryOutと呼ぶ。- recoveryOutのscriptPubKeyに
<recovery-sPK-hash>
と同じタグ付きハッシュがない場合 (tagged_hash("VaultRecoverySPK", recoveryOut.scriptPubKey) == recovery-sPK-hash
、ここでtagged_hash()
は BIP-340の参照コードのもの)、 スクリプトの実行は失敗し直ちに直ちに終了しなければならない。- 実装の推奨事項:recoveryOutにこのインプットの量以上の
nValue
がない場合は、スクリプトの実行は失敗し直ちに直ちに終了しなければならない。
- 実装の推奨事項:recoveryOutにこのインプットの量以上の
- 遅延キューに入れて、recoveryOutの
nValue
にこのインプットの全nValue
が含まれていることを確認する。- この遅延チェックは、次の疑似コードの観点から
RecoveryCheck(<recovery-vout-idx>, インプットの量)
と特徴づけられる。
- この遅延チェックは、次の疑似コードの観点から
どの条件も失敗しない場合、単一のtrue値(0x01
)がスタック上に残る。
トランザクションのすべてのインプットが上記のルールに従って検証されたら、 キューに入れられた遅延チェックを評価する必要がある。
このためのPythonの疑似コードは次のようになる:
class TriggerCheck:
"""OP_VAULT(引き出しのトリガー)の評価によってキューに登録される"""
input_amount: int
revault_amount: int
trigger_vout_idx: int
revault_vout_idx: int
class RecoveryCheck:
"""OP_VAULT_RECOVERの評価によってキューに登録される"""
input_amount: int
vout_idx: int
def validate_deferred_checks(checks: [DeferredCheck], tx: Transaction) -> bool:
"""
トリガーまたはリカバリーされるVaultインプットのすべての金額が適切なアウトプットのnValueに保存されていることを保証する
"""
# 期待されるアウトプットの値を保持するマップ
out_map: Dict[int, int] = defaultdict(lambda: 0)
for c in checks:
if isinstance(c, TriggerCheck):
out_map[c.trigger_vout_idx] += (c.input_amount - c.revault_amount)
if c.revault_amount > 0:
out_map[c.revault_vout_idx] += c.revault_amount
elif isinstance(c, RecoveryCheck):
out_map[c.vout_idx] += c.input_amount
for (vout_idx, amount_sats) in out_map.items():
# トリガー/リカバリーの値は、Vaultのインプットの量より大きくなる可能性がある。
if tx.vout[vout_idx].nValue < amount_sats:
return False
return True
上記の手順または同等の手順がfalseを返した場合、スクリプトの実行は失敗し直ちに直ちに終了しなければならない
これにより、すべての互換性のあるVaultインプットを、インプットの値すべてを保持しながら、 共有の対応するトリガーまたはリカバリーアウトプットにバッチ処理できることが保証される。
起こり得るPinning攻撃を防ぐため、リカバリートランザクションは置換可能である必要がある。
- 使用される
OP_VAULT_RECOVER
インプットを検証する際、以下の両方である場合スクリプトは(コンセンサスではなくポリシーで)失敗し、直ちに終了する必要がある。- インプットのnSequence番号を
0xffffffff - 1
未満(オプトインの置換可能性の印)にしていない場合。 - リカバリートランザクションのnVersionが3以外の場合
- インプットのnSequence番号を
OP_VAULT_RECOVER
を含むスクリプトが34バイト以下である場合、
リカバリープロセスを保護するスクリプトがないため、「無許可」と呼ばれる。
無許可のリカバリーの場合のPinning攻撃を防ぐため、
インプットの使用(およびそのトランザクションの構造)は署名された署名メッセージによって認可されていないので、
無許可のリカバリートランザクションのアウトプットの構造は制限されている。
- リカバリーが無許可の場合、リカバリートランザクションは、(ポリシーにより)以下の制約に従わなければならない。
- 支払いトランザクションに3つ以上のアウトプットがある場合、スクリプトは失敗し直ちに終了しなければならない。
- 支払いトランザクションが2つのアウトプットを持ち、recoveryOutではないアウトプットが エフェメラルアンカーではない場合、 スクリプトは失敗し直ちに終了しなければならない。
サンプル実装は関連するプルリクエストと合わせて bitcoin-inquisitionで入手できる。
おそらく驚くべきことに、上記の仕様では、固定ターゲットによる相対的なタイムロックされた引き出しプロセスがどのように実装されるか具体的に説明していない。
OP_VAULT
で指定されたTapleafの更新セマンティクスとOP_VAULT_RECOVER
によって有効化されるアウトプットベースの認可を使用してVaultを実装できるが、
これらは他の2つのパーツがなければ不完全だ:
OP_CHECKSEQUENCEVERIFY
のような相対的タイムロックを強制する方法- 提案された引き出しが最終的に
OP_CHECKTEMPLATEVERIFY
などの正確なアウトプットのセットに使用されるに強制する方法
これら2つのパーツは、OP_VAULT
のTapleafの更新機能と組み合わされ、以下で説明するVaultを作成する。
コインを保管するには、以下の形式のTaptreeを含むwitness v1 scriptPubKey
にコインを送金する。
tr(<internal-pubkey>,
leaves = {
recover:
<recovery-sPK-hash> OP_VAULT_RECOVER,
trigger:
<trigger-auth-pubkey> OP_CHECKSIGVERIFY (i)
<spend-delay> 2 $leaf-update-script-body OP_VAULT, (ii)
... [ possibly other leaves ]
}
)
ここで、
$leaf-update-script-body
は、たとえばOP_CHECKSEQUENCEVERIFY OP_DROP OP_CHECKTEMPLATEVERIFY
。- これは、トリガースクリプトの一例だが、任意のスクリプトの断片を使って様々なタイプのVaultを作成できる。
たとえば、
OP_CHECKSEQUENCEVERIFY OP_DROP OP_CHECKSIG
として、時間遅延を設けてコインを別の鍵に転送できる。 これにより、OP_VAULT
は将来のスクリプト機能にも対応できるようになる。
- これは、トリガースクリプトの一例だが、任意のスクリプトの断片を使って様々なタイプのVaultを作成できる。
たとえば、
(i)
内のスクリプトの断片は、引き出しのトリガーを制御するため「トリガー認可」と呼ばれる。これはウォレット設計者の希望する方法で行える。(ii)
内のスクリプトの断片は、不完全なOP_VAULT
呼び出し。残りのパラメーター(CTVのターゲットハッシュ、トリガーvoutインデックス、 revault voutインデックッス)が、トリガートランザクションのwitnessで提供されると完成する。
通常、VaultのTaprootアウトプットの内部鍵はリカバリーパスと同じディスクリプターによって制御されるように指定される。 これにより、Vaultアウトプットをリカバリパスにリカバリーする別の(おそらく未使用の)手段が容易になる。 これには、Vaultであることを決して明らかにすることなく、コインを回収できるという潜在的な利点がある。
それ以外の場合は、Taptreeの内容を強制的に実行するために、内部鍵を使用不可能なNUMSポイントにする。
Vaultを使用し、それを何らかのアウトプットに支払うには、単純にtriggerリーフを
完全なleaf-updateスクリプト(この場合はタイムロックされたCTVスクリプト)に置き換える
上記のtr()
アウトプットを使用する。
Witness stack:
- <revault-amount>
- <revault-vout-idx> (しない場合は-1)
- <trigger-vout-idx>
- <target-CTV-hash>
- <trigger-auth-pubkey-signature>
- [ "trigger"リーフスクリプトの内容 ]
- [ Taprootのcontrol blockはtriggerリーフを使用するためのスクリプトパスの使用する]
Output scripts:
[
tr(<internal-pubkey>,
leaves = {
recover:
<recovery-sPK-hash> OP_VAULT_RECOVER, <-- 変更なし
trigger:
<target-CTV-hash> <spend-delay>
OP_CHECKSEQUENCEVERIFY OP_DROP OP_CHECKTEMPLATEVERIFY <-- OP_VAULTのleaf-updateルールに従って変更される
... [ possibly other leaves ]
}
),
[ 元のVaultアウトプットと同じscriptPubkeyを持つオプションのRevaultアウトプット],
]
OP_VAULT
により、triggerリーフがタイムロックされたCTVスクリプトにTaptreeを変換でき、
これにより発表された引き出しが実際に容易になる。recoverリーフは元のTaptreeから正確に保存されるため、
引き出しはリカバリーパスによって中断できる。
CTVハッシュは、witnessスタックを使用して支払時に指定され、アウトプット内でその存在を検証する
OP_VAULT
の使用ルールによってロックインされることに注意。
Vaultの資金は、recoverリーフを使用したスクリプトパスの使用により、 タイムロックされたCTVスクリプトの使用前にいつでもリカバリーできる。
Vaultを設定する際、ユーザーはrecoverリーフ内のOP_VAULT_RECOVER
命令の前に付加されるスクリプトの断片によって
リカバリープロセスを制御するかどうか決める必要がある。この使用にはトレードオフが伴う。
未認可のリカバリーは、VaultのOutPointとリカバリーパスの場所以外の追加情報が必要ないため、Vaultの使用がシンプルになる。
認可は単にリカバリーパス、つまり、<recovery-sPK-hash>
のプリイメージを明らかにするだけ。
ただし、この公開は、Vaultコインをリカバリーするために必要な唯一の認可であるため、 観察者は(OutPointを知っていれば)このリカバリーをリプレイできるため、 ユーザーはそのようなVaultを一度にリカバリーすることを期待する必要がある。
さらに、複数の異なるリカバリーパスにわたる未認可のリカバリーを同じトランザクションで実行することはできず、 手数料制御はより制限される。これは、未認可のリカバリーに対してアウトプットの構造が制限されているため、 手数料管理は、手数料に使用されるインプットまたはオプションのエフェメラルアンカーやパッケージリレーの使用に依存する。
これらの制限はPinning攻撃を回避するためのもの。
認可されたリカバリーでは、結ーザーは追加情報、 つまりリカバリーが必要なときにリカバリー認可スクリプトの断片を解決する方法を記録しておく必要がある。
この鍵を紛失すると、ユーザーはコインのリカバリープロセスを開始できなくなる。 攻撃者がリカバリー鍵を入手した場合、低手数料率のリカバリートランザクションを構築しブロードキャストすることで、 リカバリープロセス中にユーザーを邪魔する可能性がある(ただし、リカバリートランザクションには 置換可能性要件があるため、Pinningすることはできない)。
ただし、認可されたリカバリーの設定には大きな利点がある。 互換性のないリカバリーパラメーターを持つVaultのバッチリカバリーが可能になる。 認可されたリカバリートランザクションは、自由形式であり、手数料を処理するために無関係なインプットとアウトプットを追加できるため、 手数料管理ははるかに柔軟になる。
認可されたリカバリーが提供するバッチ処理と手数料管理の利点はとても大きい。 リカバリー認可鍵が攻撃者の手に渡った場合でも、致命的な結果にはならないが、 ユーザーがリカバリー認可鍵とトリガー鍵を紛失した場合は、コインの損失が発生する可能性がある。 したがって、作成者は、オフラインで書き留めて、複製できる単純なシードをリカバリー認可鍵に使用することを推奨する。
リカバリー認証鍵は、リカバリーパス鍵ではない。これはリカバリーパス鍵自体の精製方法に関する推奨事項とは大きく異なることに注意。
Vaultを作成する際、4つのファクターが生成されるP2TRアドレスに影響する:
- 内部鍵(リカバリーウォレットにある可能性がある)
- リカバリーリーフ
- トリガーリーフ
- Taptree内に存在するその他のリーフ
エンドユーザーは、(トリガー認可公開鍵などの)鍵管理に影響を与えることなくVaultアドレスの再利用を避けるために、 ディスクリプターに沿って特定の内容を変更する変更する選択肢を持っている。
未認可のリカバリーを使用する場合、リカバリーのscriptPubKeyの公開により、
VaultのOutPointを見つけることができれば、観察者はリカバリーパラメーターが一致するVaultのリカバリープロセスを開始できることに注意してほしい。
その結果、同一の未認可の<recovery-sPK-hash>
を共有するすべてのアウトプットが一緒にリカバリーされることを期待することを推奨する。
この状況は、単一のディスクリプターに沿った各VaultのリカバリーscriptPubKeyの生成を変更することで回避できるが、 これにより、複数の個別のVaultを単一のリカバリーアウトプットにリカバリーできなくなることに注意してほしい。
内部鍵を変更すると、複数のVaultインプットのトリガーを単一のトリガーアウトプットにバッチ処理することができなくなる。 したがって、アドレスの再利用が望ましくない場合は、代わりにtrigger leafスクリプトの一部の内容を変更することを推奨する。 ユーザーはディスクリプターに沿ってトリガー公開鍵を変更し、リカバリーパスと内部公開鍵を同じに保つことができ、 これによりアドレスの再利用が回避され、トリガーおよびリカバリー操作のバッチ処理が可能になる。
未認可のリカバリーを使用する場合、個別のトリガー鍵間でリカバリーのscriptPubkeyを共有しないことを推奨する。 1つのトリガー鍵が侵害されると、そのトリガー鍵を使用するすべてのVaultの未認可のリカバリーをする必要gああり、 これによりリカバリーパスのプリイメージが明らかになる。これは観察者が侵害されていないトリガー鍵によって制御されている Vaultのリカバリーを開始できる可能性があることを意味する。
手数料はさまざまな方法で管理できるが、トリガートランザクションとリカバリートランザクションの両方で Vaultインプットの合計値を保持する必要があるので、Vaultに保管された資金を手数料の支払いに再利用できないことに注意してほしい。 これは、任意の金額を割り当てることが可能な引き出しトランザクションには適用されない。
リカバリー認可を使用するVaultの場合、すべてのトランザクションは無関係のインプットとアウトプットという形で 独自の手数料を設ける可能性がある。これらのトランザクションは関連するリレーポリシーが展開されると、 エフェメラルアンカーを自由に指定することもできる。これは、リカバリー認可を使用するVaultがv3リレーポリシーの展開に依存しないことを意味する。
未認可のリカバリーを使用するVaultの場合、リカバリートランザクションはインプットをすべて手数料として使用するか、 エフェメラルアンカーアウトプットの使用に依存する。これは、 リカバリー認可を使用しないVaultは基本的に、展開されるv3リレーポリシーに依存していることを意味する。
同じTaptreeを持つOP_VAULT
アウトプットは、わずかにトリガーのリーフが異なることを除けば、
同じ引き出しプロセスで一緒にバッチ処理できる。2つのトリガーリーフは、同じOP_VAULT
引数を持つ場合、互換性がある。
これにより、バッチ処理を可能にしながら、トリガーの認可(OP_VAULT
呼び出しの前に付与されるスクリプト)を
変更できることに注意してほしい。
各セットに適切な関連triggerOutアウトプットがある場合、
トリガートランザクションは複数の互換性のないOP_VAULT
インプットのセットで動作できる。
SIGHASH_DEFAULT
は、トリガー認可に使用できるため、無関係のインプットとアウトプットを含めることができ、
おそらく手数料管理や互換性のないVaultのバッチ引き出しを容易にすることができる。vaults.
最後の引き出し中、同一の<target-CTV-hash>
パラメーターを共有する場合、
同じ引き出しトランザクションで複数のトリガーアウトプット使用できる。
同じ<recovery-sPK-hash>
を持つOP_VAULT_RECOVER
アウトプットは、
同じアウトプットにリカバリーできる。
認可されたリカバリーを持つリカバリー非互換Vaultは、(<recovery-sPK-hash>
によってグルーピングされる)
各セットに関連付けられたrecoveryOutがある限り、同じトランザクションでリカバリーできる。
これにより、無関係のリカバリーで共通の手数料管理を共有できる。
Vaultの価値は、予期せぬ支払いが起きたときに所有者に警告する監視を導入しているかどうかによって決まる。 これは、自動化やウォッチタワーへのトラストにより、さまざまな方法で実行できる。
トラストを最大にする場合、ウォッチタワーは保管されているすべてのコインを完全に認識でき、 使用が事前にウォッチタワーに報告されていない場合、リカバリープロセスを開始できる手段を持っている。
トラストを最小にする場合、ユーザーは監視したいコインの確率フィルターを提供できる。 ウォッチタワーは、フィルターに一致するコインが移動した場合にユーザーに警告する。 ユーザーは誤検知を無視してリカバリーの開始を処理する責任がある。
Vault関連のアウトプット用のアウトプットディスクリプターは、後続のBIPでカバーされるだろう。
アクティベートの仕組みはこれから決定される。
Vaultを最大限に活用できるようにするには、このBIPをBIP-119と同時に展開する必要がある。
OP_VAULT
とOP_VAULT_RECOVER
は、それぞれwitness v1のopcode OP_SUCCESS187とOP_SUCCESS188を
より厳格な検証セマンティクスに置き換える。したがって、以前は有効であったこれらのopcodeを使用するスクリプトは、
この変更により無効になる。
OP_SUCCESSx opcodeに対するより厳格な検証セマンティクスはソフトフォークであるため、 既存のソフトウェアは(マイニングとブロック検証を除き)アップグレードしなくても完全に機能する。
後方互換性に関する考慮事項は、OP_CHECKLOCKTIMEVERIFYと OP_CHECKSEQUENCEVERIFYの以前の 展開とよく似ている。
- [bitcoin-dev] Bitcoin Vaults (2016)
- [bitcoin-dev] Simple lock/unlock mechanism (2018)
- [bitcoin-dev] On-chain vaults prototype (2020)
- [bitcoin-dev] TAPLEAF_UPDATE_VERIFY covenant opcode (2021)
- Custody Protocols Using Bitcoin Vaults (2020)
- Vaults and Covenants (2023)
著者は以下を感謝している。
- AJ TownsとGreg Sandersとの議論において、提案を改善するための多数の提案とアドバイスを提供してもらう
- Jeremy Rubinからのインスピレーション、アドバイス、指導
- BLとの議論と洞察
- John Moffettには、早期のフィードバックと再帰的スクリプト評価攻撃を実証するテストケースの提供を。
- Johan Halsethには、概念的なレビューとPinning攻撃についての指摘を。
- Pieter Wuilleには実装上のアドバイスを。