現在BitcoinにはSIGHASH_ALL
、SIGHASH_SINGLE
、SIGHASH_NONE
の3つにSIGHASH_ANYONECANPAY
を組み合わせた系6バターンのSIGHASHの組み合わせがあるが↓
techmedia-think.hatenablog.com
これにSIGHASH_NOINPUT
という新しいSIGHASHフラグを追加しようというBIP-118が提案された↓
https://github.com/bitcoin/bips/blob/master/bip-0118.mediawiki
これまでのSIGHASHフラグの署名はいずれもインプットのデータにコミットしてきた。インプットのデータは↓
- OutPoint
(使用するUTXOの参照先のデータ(32バイトのTXIDとそのアウトプットのインデックス4バイト)) - nSequence
- scriptSig
この内scriptSig
は署名自体を格納するデータ領域なので除外して、OutPointとnSequenceにコミットしてきた。またこれに加えて、OutPointの参照先であるアウトプットのスクリプトにもコミットしている。(コミットしているというのは、署名を生成する際の署名対象のデータ(トランザクションダイジェスト)に、これらの値が含まれているということ。)
そのため、署名済みのトランザクションについて、そのインプットが参照するOutPointの値を変更すると、署名が無効になり、無効なトランザクションになっていた。
BIP-118で提案されているSIGHASH_NOINPUT
は、このインプットの参照へのコミットメントを外せるようにしようというもので、今後のSegwitのwitness versionを更新するタイミング(バージョン1以降のSegwitスクリプト)での導入を提案している。
具体的には、Segwitトランザクションで署名を生成する際のトランザクションダイジェストは現在BIP-143のルールに従って生成されているが↓
techmedia-think.hatenablog.com
SIGHASH_NOINPUT
が指定されていた場合は、このトランザクションダイジェストの生成アルゴリズムを変更して、hashPrevouts
とhashSequence
、scriptCode
を全部空にする(詳細は下記仕様参照)。
こうすると、署名済みトランザクションのインプットの参照先のOutPointを別のものに変更しても、署名は有効なままになる。この参照先を変更することをリバインディングと呼んでいる。
ただ、当然参照先をどんなアウトプットにでも変更できるという訳ではない。トランザクションのwitness
にはリバインディング前のアウトプットのscriptPubkey(witness program
)をアンロックするための要素がセットされているので、同じwitness
のアイテムでアンロック可能なアウトプットのみリバインディングが可能となる。
SIGHASH_NOINPUT
は、通常のオンチェーンでビットコインを送金する際には使うことはないが、先日Blockstreamが発表した新しいLNのプロトコル「eltoo」などが実装できるようになる↓
techmedia-think.hatenablog.com
現状はまだBIPのドラフトが公開されたばかりで、デプロイ時期は次のSegwitバージョンに合わせるということで未定、参照実装もまだ無いので、導入に向けては今後の反応を待つ感じかな?
また、SIGHASH_NOINPUT
のフラグの値が0x40
と、フォークコインのリプレイ保護で導入されたFORK_ID
のSIGHASHフラグと値が被ってるが、MLのポストみた感じだと適用するのはSegiwtトランザクションだから影響はないってことでそのまま採用の流れか?
以下BIPの仕様の意訳↓
仕様
SIGHASH_NOINPUT
は値0x40
のフラグで、署名がインプットのいずれにもコミットしない(=使用されるアウトプットにコミットしない)。このフラグは単一の署名検証にのみ適用される。
SIGHASH_NOINPUT
はバージョン1以上のSegwitスクリプトに対してのみ有効で、このフラグが非Segwitスクリプトもしくはバージョン0のSegwitスクリプトに対して使用された場合、現在の動作はそのままでスクリプトの実行は失敗して中断しなければならない。
SIGHASH_NOINPUT
の署名を検証する際は、BIP-143のトランザクションダイジェストアルゴリズムが使われるが、以下の点が異なる。
2. hashPrevouts (32バイトのハッシュ) は32バイトの0x00になる。 3. hashSequence (32バイトのハッシュ) は32バイトの0x00になる。 4. outpoint (32バイトのハッシュ + 4バイトのリトルエンディアン)には36バイトの0x00がセットされる。 5. インプットのscriptCodeには空のスクリプト0x00がセットされる。
前のアウトプットのvalue(Bitcoinの量)は、トランザクションダイジェストの一部としてそのまま残っているので、署名にもコミットされる。
NOINPUT
フラグはSINGLE
フラグと組み合わせて使ってもよく、この場合hashOutputs
はBIP-143に従って変更され、インプットと同じインデックス以外のアウトプットについてはuint256
0x0000......0000
になる。
ダイジェストアルゴリズムの変更になるため、NOINPUT
フラグは全てのSegwitの署名検証opcodeに適用される。具体的には以下のopcodeに適用される。
OP_CHECKSIG
OP_CHECKSIGVERIFY
OP_CHECKMULTISIG
OP_CHECKMULTISIGVERIFY
スクリプトによるバインド
NOINPUT
を使うとその署名が含まれているインプットは特定のアウトプットを参照しなくなる。参加者はトランザクションを受け取り、セットされている署名を無効にすることなくインプットが参照する前のアウトプットのハッシュリファレンスを書き換えることができる。これにより、トランザクションは、インプットの参照先をwitness
やwitnessProgram
にコミットされた値とマッチする任意のアウトプットにバインドすることができ、新しいバインド先のアウトプットと組み合わせたトランザクションのwitness
の結果はtrueを返す。
以前は、トランザクション内の全ての情報が署名自体にコミットされていたが、これからはトランザクションと使われるアウトプットの関係は、witnessProgram
とwitness
の互換性にのみ基づくようになる。
これはまた、このリバインディングの仕組みを誤って有効にしないように、特別な注意を払わないといけないことを意味する。NOINPUT
はアプリケーションに明示的に必要とされない限り有効にしてはならない。つまりウォレットの実装でデフォルトの署名フラグにしてはならない。リバインディングは、トランザクションにバインドできるアウトプットがすべて同じ公開鍵を使っている場合のみ可能だ。NOINPUT
で使用されている公開鍵は、インプットがバインド可能なアウトプットにのみ使用する必要があり、インプットがバインド出来ないトランザクションには使用しないこと。例えば、アプリケーションはNOINPUT
署名を使用するアプリケーション用に新しいキーペアを生成し、後でそれを再利用してはならない。
デプロイメント
NOINPUT
SIGHASHフラグは通常のSegwitスクリプトの更新中にデプロイされる。
後方互換性
ソフトフォークなので、旧ソフトウェアは変更することなく引き続き動作する。ただしアップグレードされていないノードでは、新しいSIGHASHフラグは検証されず、デフォルトでトランザクションは有効と判断される。Segwitトランザクションにのみ適用可能であるため、非Segwitノードには誰でも使用可能なスクリプトととして表示され、有効と判断される。