CheckSigFromStack for 5 Byte Values
最近、BitcoinでのCovenantsに関するブログ記事を公開した。 読者は、Bitcoinで現在CheckSigFromStackを実行できると主張したことについて、十分に説明していなかったことをすぐに指摘した。
そのため、アーカイブのために、技術を完全に説明することに価値があると思った。
この投稿には、2つの洞察がある:
* 数値のビット単位の展開を使用する
* ランポート署名を使用する
Pythonのコードを見たのち、Bitcoin Scriptに変換してみよう:
code: python
def add_bit(idx, preimage, image_0, image_1):
s = sha256(preimage)
if s == image_1:
return (1 << idx)
if s == image_0:
return 0
else:
assert False
def get_signed_number(witnesses : ListHash, keys : List[TupleHash, Hash]): acc = 0
for (idx, preimage) in enumerate(witnesses):
acc += add_bit(idx, preimage, keysidx0, keysidx1) return x
ここでは何をしているのか?署名者は、スクリプトを作るための、ハッシュイメージのペアのリストであるキーを生成する。
署名するためには、署名者はどちらか一方に合致するプリイメージのリストのwitnessを提供する。
検証中、ネットワークはプリイメージ毎に重み付けされた値を合計し、除外された値がないことをチェックする。
具体的なユースケースを想像してみよう。サードパーティにシーケンスロックに事後署名してもらいたい。これは16bit。次のスクリプトを作成できる:
code:python
<pk> checksigverify
0
SWAP sha256 DUP <H(K_0_1)> EQUAL IF DROP <1> ADD ELSE <H(K_0_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_1_1)> EQUAL IF DROP <1<<1> ADD ELSE <H(K_1_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_2_1)> EQUAL IF DROP <1<<2> ADD ELSE <H(K_2_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_3_1)> EQUAL IF DROP <1<<3> ADD ELSE <H(K_3_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_4_1)> EQUAL IF DROP <1<<4> ADD ELSE <H(K_4_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_5_1)> EQUAL IF DROP <1<<5> ADD ELSE <H(K_5_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_6_1)> EQUAL IF DROP <1<<6> ADD ELSE <H(K_6_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_7_1)> EQUAL IF DROP <1<<7> ADD ELSE <H(K_7_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_8_1)> EQUAL IF DROP <1<<8> ADD ELSE <H(K_8_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_9_1)> EQUAL IF DROP <1<<9> ADD ELSE <H(K_9_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_10_1)> EQUAL IF DROP <1<<10> ADD ELSE <H(K_10_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_11_1)> EQUAL IF DROP <1<<11> ADD ELSE <H(K_11_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_12_1)> EQUAL IF DROP <1<<12> ADD ELSE <H(K_12_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_13_1)> EQUAL IF DROP <1<<13> ADD ELSE <H(K_13_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_14_1)> EQUAL IF DROP <1<<14> ADD ELSE <H(K_14_0)> EQUALVERIFY ENDIF
SWAP sha256 DUP <H(K_15_1)> EQUAL IF DROP <1<<15> ADD ELSE <H(K_15_0)> EQUALVERIFY ENDIF
CHECKSEQUENCEVERIFY
16 bitの値Vに署名するために、Kの所有者は単純にスタックにKにインデックスされたVのバイナリ表現をプッシュするだけ。例えば53593に署名する場合、まずはバイナリ展開する0b1101000101011001。次に適切なKの値をスタックにプッシュする。
code: python
K_15_1
K_14_1
K_13_0
K_12_1
K_11_0
K_10_0
K_9_0
K_8_1
K_7_0
K_6_1
K_5_0
K_4_1
K_3_1
K_2_0
K_1_0
K_0_1
<sig>
この手法は、ちょっとかさばる。ガジェットの長さは、80✕16 = 1280バイトで、witnessの長さは528バイト。ちょっと大きいがなんとかなる。もっと効率的なスクリプトがあるかもしれない。3進表現の方が効率的?
署名可能な値の範囲はOP_WITHINを使って事後的に制限することもできるし、16 bit値回路のように内部的に16bit以上はできないよう制限することも可能。
鍵はスクリプト間で再利用できるが、署名は1回しか作れない。なぜなら、サードパーティが2つの署名されたメッセージを受け取り、意図しない値を作成できるからだ(例えば、4と2の両方に署名した場合、サードパーティは6を構築できる)。
例えば、K_i_0とK_i_1のいずれかを持っていると、資金を焼灼できるという契約をオラクルが結んでいるような場合、これを効果的に利用できるアプリケーションがある。