[bitcoin]トランザクションの構造
はじめに
Bitcoinリハビリということで、まずはトランザクション構造から復習しよう。
まだ新しい情報を吸収していないので、今回のトランザクションはsegwit導入された時期の知識を元に書いていく。Bech32mがmasterにマージされたという情報は入ってきたが、v0.21にはまだ入っていないし、そんなに変わってないんじゃなかろうか。
Bitcoinのトランザクション構造はプロトコルのページを見ている。
Bitcoin DeveloperのTransactionsにも説明はあるのだが、こっちはsegwitとか載っていないのだ。
トランザクション構造はsegwitか非segwitかでちょっと異なる。
どうやって見分けるかというと、versionの次が0x00だったらsegwit、そうじゃなかったら非segwit、ということになっている。txin[]が存在しないことは無いため、txin_countは必ず0より大きい。それが0ということはsegwitだ、というようなイメージだ。BIP-144のmarkerが”Must be zero”というのがそこだ。
図で太枠にしているのは、TXID(Transaction ID)の計算に用いる部分である。非segwitは全部、segwitはmarker, flag, そしてscript_witnessesを除いた部分だ(BIP-144 Hashes)。segwitのトランザクションではINPUTの署名がscript_witnessesに入っているのだが、それがなくてもTXIDまでは計算できることになる。marker, flag, script_witnessesも入った全部入りのハッシュ値はWTXID(Witness ID)と呼ばれる。
細かく見ていっても面白くないだろうし、詳細はリファレンスを読んでほしいので、ここではざっと説明するにとどめよう。
用語はプロトコルページのものを使っている。たとえばBIP-144では”script_witnesses”なのだが、プロトコルページでは”tx_witnesses”なのだ。
tx_in
VINなどと呼ばれたりもする。このトランザクションへのINPUTになるTXIDとtx_outのindex(まとめてoutpointなどとも呼ぶ)、そのINPUTに対応するscriptおよびsequenceの数字が載っている。
非segwitではscriptのところが scriptSig
と呼んでて、ここに署名が載っていた。正確には署名というかoutpointのスクリプトを解くためのスクリプトが載る。
Bitcoinの送金というのはamountの所有権の移動で、所有権というのがoutpointのスクリプトを解くことができる人、ということになっている(なのでINPUTになるトランザクションに書かれているスクリプトをlocking script、解く方のスクリプトをunlocking scriptと呼んだりもする)。標準のやり方ではlocking scriptに対応した秘密鍵での署名をunlocking scriptに載せて解くようになっているので「秘密鍵は人に見せたらダメ」と言われているのであった。
sequenceは、ちょっと説明するのが難しい。が、最近では特に重要になってきていると思う。
これ↑はプロトコルページのsequenceのリンク先だ。answerが2つあって、1つが2011年、もう1つが2017年だ。最初はBIP-65、BIP-68、BIP-112でHTLCなどで使うようなイメージだったと思う。しかし今はanswerの2つめのようにRBF(Replace-By-Free)でFeeを変更したトランザクションを展開するかもしれないサインとして使われることもあるようだ。Bitcoin CoreのGUIアプリで送金するときもRBFにして送金するかどうかのチェックボックスがあったように思う。
ついでだが、Fee不足に対応する方法としてCPFP(Child Pay For Parent)というのもある。私のイメージだと、親父が飲み屋に行って飲んでたけど、現金が足りなくなったので電話して子供に持ってきてもらうような感じだ。お酒はほどほどに。
tx_out
金額とlocking scriptを書く部分である。
tx_inのスクリプトは対応するトランザクションのtx_outと組み合わせてチェックされるのだが、tx_outのスクリプトはまだ対応するものが無いので文法チェック以外できることがない。何の話をしているかというと、自分でトランザクションを作ってBitcoinネットワークに展開する際、tx_outのスクリプトを作り間違えると誰も解けなくて宙に浮いてしまいますよ、ということだ。
私も初めてBitcoinのライブラリを作るときにはだいたい間違えてて、秘密鍵を持ってるつもりなのにスクリプトを解けないということがしばしばあった。だからregtestとかtestnetがあるのだけどね。
間違ってても何が間違っているかわからないので、私はbitcoindにログを埋め込んで、regtestでスクリプトのスタックをひたすら出力させながらデバッグしてた。泥臭いやり方だが、未だに他のやり方は思いつかない。
tx_witnesses
segwitトランザクションのみ存在する。
tx_inの数だけ存在するが、1つのtx_inに対して複数の部品が対応している。非segwitの場合は scriptSig
にunlocking scriptがまとめられているが、それがtx_witnessesに移動して平たくなったというところだろうか。
たとえばtx_inが2つあって、1番目は解くのにデータが4つ、2番目は3つ必要だった場合、tx_witnessesとしては2つだけど、各tx_witnessはアイテム数が4つと3つになっている、という感じだ。
なので私が実装するときはtx_witnessesはtx_inの一部で場所が違うだけ、という扱いにしている。作りが違うだけで情報量は同じなのだ。
lock_time
tx_inのsequenceと同じような扱いだと思っている。ただRBFみたいな新たな役割は持たないようで、HTLCとかで使うのは今まで通りのようだ。
おわりに
ざっとした感じでBitcoinトランザクションの説明をした。
bitcoin-cliでトランザクションのデコードをしたことがあるとわかるが、あれは実際のトランザクションデータが持つ以上の情報を出力してくれる。bitcoindが他のトランザクションのデータも持っているからできることだ。1つのトランザクションデータだけだと、大して情報を持たないことが分かるだろう。
だから、トランザクションデータが正しいかどうかを知るためには、過去のブロック全部とは言わないまでも、そのINPUTのトランザクションデータの存在や、トランザクションを承認したというブロックが本当に存在するのかとか、いろいろ揃わないと足りないのだ。
株式会社Nayuta
Bitcoinの2nd Layer技術-ライトニングネットワークのプロトコルとアプリケーションの開発を両輪で行う。この分野において当社は世界的認知を得、コントリビュータとしてグローバルなコミュニティで活動を継続中。最新のリリースの中には利便性とセキュリティを両立したSPVとフルノードのハイブリッドモデルNayuta Coreがある。
また、様々なタイプのブロックチェーンが共存する時代が近づいていることから、エンタープライズ向けブロックチェーンに関するビジネスも行っている。