Develop with pleasure!

福岡でCloudとかBlockchainとか。

新しいSIGHASHフラグ「SIGHASH_NOINPUT」を定義したBIP-118

現在BitcoinにはSIGHASH_ALLSIGHASH_SINGLESIGHASH_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が指定されていた場合は、このトランザクションダイジェストの生成アルゴリズムを変更して、hashPrevoutshashSequencescriptCodeを全部空にする(詳細は下記仕様参照)。

こうすると、署名済みトランザクションのインプットの参照先の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を使うとその署名が含まれているインプットは特定のアウトプットを参照しなくなる。参加者はトランザクションを受け取り、セットされている署名を無効にすることなくインプットが参照する前のアウトプットのハッシュリファレンスを書き換えることができる。これにより、トランザクションは、インプットの参照先をwitnesswitnessProgramにコミットされた値とマッチする任意のアウトプットにバインドすることができ、新しいバインド先のアウトプットと組み合わせたトランザクションwitnessの結果はtrueを返す。

以前は、トランザクション内の全ての情報が署名自体にコミットされていたが、これからはトランザクションと使われるアウトプットの関係は、witnessProgramwitnessの互換性にのみ基づくようになる。

これはまた、このリバインディングの仕組みを誤って有効にしないように、特別な注意を払わないといけないことを意味する。NOINPUTはアプリケーションに明示的に必要とされない限り有効にしてはならない。つまりウォレットの実装でデフォルトの署名フラグにしてはならない。リバインディングは、トランザクションにバインドできるアウトプットがすべて同じ公開鍵を使っている場合のみ可能だ。NOINPUTで使用されている公開鍵は、インプットがバインド可能なアウトプットにのみ使用する必要があり、インプットがバインド出来ないトランザクションには使用しないこと。例えば、アプリケーションはNOINPUT署名を使用するアプリケーション用に新しいキーペアを生成し、後でそれを再利用してはならない。

デプロイメント

NOINPUT SIGHASHフラグは通常のSegwitスクリプトの更新中にデプロイされる。

後方互換

ソフトフォークなので、旧ソフトウェアは変更することなく引き続き動作する。ただしアップグレードされていないノードでは、新しいSIGHASHフラグは検証されず、デフォルトでトランザクションは有効と判断される。Segwitトランザクションにのみ適用可能であるため、非Segwitノードには誰でも使用可能なスクリプトととして表示され、有効と判断される。