用語集 / トランザクションと手数料
SIGHASH_ALL
- SIGHASH_ALLとは?
- SIGHASH_ALLは、署名がトランザクションのすべての入力とすべての出力に対してコミットするようにするフラグであり、そのため署名後には支払いに関するいかなる内容も変更できません。
これはウォレットが使用するフラグであり、ハードウェアウォレットの画面でトランザクションを承認することに意味がある理由でもあります。値は1で、ブロックエクスプローラー上ではほとんどすべての署名の最後のバイトとして確認できます。Taprootはデフォルトの名称をSIGHASH_DEFAULTに改め、このバイトをまったく書かなくなり、署名1件あたり1バイトを節約しました。
仕組み
SIGHASH_ALLでは、ダイジェストはトランザクションのバージョン、消費されるすべてのアウトポイント、すべてのシーケンス番号、すべての出力金額、すべての出力スクリプト、そしてロックタイムを対象とします。
1つの出力の1サトシでも変更すれば、そのトランザクションのすべての署名は無効になります。入力の追加、出力の削除、並べ替え、ロックタイムのわずかな変更も、結果は同じです。署名済みのビットコイントランザクションは、ブロードキャストされる前の時点で内容が確定していると人々が言うとき、それはこのことを指しています。残された唯一の判断は、送信するかどうかだけです。
意図的に除外されているものが1つあります。他の入力のアンロックスクリプトです。もしダイジェストがそれらを対象とすれば、署名者が2人いるトランザクションでは、各署名をもう一方の署名の後に行う必要が生じ、これは循環していて不可能です。それらを除外しているからこそ、独立した当事者が同じトランザクションに任意の順序で署名できます。
エンコードは入力の種類によって異なります。レガシーまたはバージョン0のsegwitの入力では、バイト01がDERエンコードされた署名に付加され、よく知られた71または72バイトという合計はここから来ています。Taprootのキーパスによる消費では、BIP341が値ゼロをSIGHASH_DEFAULTと定義しており、同じ対象範囲をバイトをまったく書かずに意味するため、署名はちょうど64バイトになります。Taprootの入力で01を明示的に書いても有効で、同じダイジェストが生成されますが、無駄に1バイト余分にかかるだけです。
どこで見られるか
SIGHASH_ALLはほとんどどこでも使われています。Bitcoin Core、Electrum、Sparrow、すべてのハードウェアウォレット、そしてすべての取引所の出金エンジンがデフォルトでこの方式で署名しており、いずれもこれを変更する設定を提供していません。
実務上の帰結は、支払いが停滞したときに実感するものです。手数料は入力と出力の差額として残るものであり、出力はすべて署名されているため、手数料を上げるために出力から数千サトシを削ることはできません。トランザクションは最初から作り直して再署名する必要があり、それができるのは鍵を保有するウォレットだけです。だからこそreplace-by-feeは送信側ウォレットの機能であり、取引所やブロックエクスプローラーが利用者に代わって実行できるものではないのです。また、送信者に連絡が取れない場合の逃げ道としてchild-pays-for-parentが存在する理由もここにあります。
これはまた、ハードウェアウォレットの画面を提案ではなく契約に変えるものでもあります。デバイスは送信先、金額、手数料を表示し、その後に生成する署名がこの3つすべてを対象とするため、下流のどのソフトウェアもその署名を破棄せずにいずれかを変更することはできません。画面に表示されたとおりに承認すれば、それ以外のことは起こりえません。
SIGHASH_ALL vs SIGHASH_NONE
SIGHASH_ALLは出力リスト全体にコミットしますが、SIGHASH_NONEはいずれにもコミットしません。両者の差は、支払いと、不特定の場所へコインを移動するだけの素の許可との差です。2つのフラグは値が1つしか離れていないため、フラグを整数パラメーターとして受け取る署名ライブラリは、タイプミス1つで顧客の出金を、mempoolを監視する誰もがリダイレクトできるコインに変えてしまいます。本番の署名コードは、値を入力として受け取るのではなくハードコードすべきです。