Bitcoinのtransactionの署名検証をscriptから理解する

ブログを書くにあたって

この度、コインチェックでは技術ブログを始めることになりました。これから不定期更新ではありますが、このブログでは主に仮想通貨やblockchainの技術周りについての情報発信を行っていけたらなと思います

 

ブログの初回を担当します、仮想通貨開発グループの善方(ぜんぽう)と申します。

 

では、早速始めて行きましょう!

 

初回となる今回は、Bitcoinのtransactionの署名検証の仕組みについて説明します。この記事を通じて、transactionの署名検証は一体なにを行なっているのか理解をし、transactionの本質を理解して行きましょう!!

 

transactionに関する記事を見たり、関連する本を参照すると、秘密鍵で署名を行なってBitcoinを自分の物にする、といった説明がされていると思います。一般的に、自分が所有する秘密鍵でtransactionに署名を行なって、そのBitcoinが自分の物であるという証明を行うのですが、実際にBitcoinのノードを触ったことがある方なら分かると思いますが、ノードに対して送金コマンドを実行すると、transactionに対して勝手に署名検証が実行されます。今回は、ここの署名検証の仕組みについて、Bitcoinのscriptの仕組みまで遡って説明します。

 

tranasctionのデータ構造

図 1

 

まずは、transactionの中身から見て行きましょう。transactionの中身は図 1の様になっています。transactionは、input群(txins)とoutput群(txouts)、その他transactionに関わる情報を保持しています。Bitcoinは、utxo(unspent transaction output)をというoutputを参照して送金処理を行うのですが、outputとそのoutputを使用するinputは接続関係を持っており、outputにはBitcoinをlockする為のscriptであるscript_pubkeyが、inputにはBitcoinをunlockするscriptであるがscript_sig含まれています。

 

図 2

 

  図 2においては、transaction1のoutput群の中のNo.0のoutputを参照する形で、transaction2のinputにoutputの内容が取り込まれています。

 

ここでは、utxoであるtransaction 1に対する署名の検証を行なっている訳なのですが、署名の検証はどういうもので、一体どの様に行なっているのでしょうか?

 

scriptとは何か?

Bitcoinにおいては、スクリプト言語を利用して署名の検証を行なっており、署名の検証とは、自分のutxoに対してデジタル署名が正しい物かどうかを、公開鍵のハッシュ値などを用いてscriptの計算により検証する事をさします。

 

outputのscript_pubkeyにはデジタル署名を検証する命令があり、inputのscript_sigにはデジタル署名をそれぞれスクリプト言語で記述します。

 

ここでいうスクリプト言語とは、一体なんなのでしょうか?

 

Bitcoinにおいては、scriptと呼ばれるスクリプト言語の一種をtransaction内に記述する事で、上記の様なデジタル署名の検証を行なっており、その部分がscript_sigやscript_pubkeyのフィールドに記載されています(図 1を参照)。

 

scriptは、stackと呼ばれる様なデータ構造をもち、データを、先入れ、後出し、で保持する様な挙動を示します。

 

図 3

 

図 3を見てください。まず最初に空のstackがあり、そのstackに対してdataを一つ追加します。すると、空のstackにdata1が追加されます。そのスタックにdataをまた追加すると、data1の上にdata2が乘る形で追加され、さらにそのstackからdataを取り出すと、data2が取り出される様な挙動を示します。

 

図 4

 

Bitcoinでは、このscriptを使用して署名の検証を行なっており、デジタル署名の検証では、scriptの実行後、stackに1(true)が残れば署名検証が成功(success)、0(false)や2つ以上のdataが残った場合や、stackが空の場合は、署名検証が失敗(failure)となります。(図 4)

 

OP_codeを理解しよう

上記で説明したscriptには、OP_code(オペコード)と呼ばれる命令が使用されます。例えば、Bitcoinのデジタル署名の検証で使用されるop_codeは下図の様になります。

 

図 5

 

一体何の事かわかりませんね。順を追って説明します。

 

Bitcoinのscript_sigやscript_pubkeyは、scriptで書かれていて、それは上図の様なop_codeと呼ばれるものであると説明しました。今回は、P2PKH(Pay to Public Key Hash)と呼ばれるscriptの中身を見てみます。

 

図 6

 

6を見て頂くと分かる通り、script_pubkeyやscript_sigの中身は、図 5のOP_codeでプログラムさている事が分かると思います。これから、図 6のscriptと図 7のstackの図を参考にして、transaction上でなされているデジタル署名の検証のscriptの実行手順を見ていきたいと思います。

 

<デジタル署名の実行手順>

①. script_pubkeyとscript_sigを連結

②. <signature> <public key>をstackへ移動

③. OP_DUPを使って、<public key> を複製

④. OP_HASH160を使って、<public key> をハッシュ化

⑤. check scriptより、stackへ新たに<public key hash>を追加

⑥. ハッシュ関数によって作成された<public key hash>と、script_pubkeyに元々あった<public key hash>を比較

⑦. OP_CHECKSIGによって、script全体の検証を行い、有効であれば ”1” を返す

 

①. script_pubkeyとscript_sigを連結 (図 6, 図 7 ①) 

script_pubkeyscript_sig、それぞれを連結して、図 6のcheck scriptの様に連結する。デジタル署名<signature>と公開鍵<public key>を使ってutxoをunlockする準備をする。この連結したものを、左から順番にstackに移動していき、実行していく。

chaeck scriptの状態: <signature> <public key> OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG

 

②. <signature> <public key>をstackへ移動 (図 7 ②)

chaeck scriptの状態: OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG

 

③. OP_DUPを使って、<public key> を複製(図 7 ③)

OP_DUPは、stackの1番上に乗っている変数を複製する関数( 5を参照)

chaeck scriptの状態: <public key hash> OP_EQUALVERIFY OP_CHECKSIG

 

④. OP_HASH160を使って、<public key> をハッシュ化(図 7 ④)

OP_HASH160は、stackの1番上に乗っている変数を160bitでハッシュ化( 5を参照) 

chaeck scriptの状態: <public key hash> OP_EQUALVERIFY OP_CHECKSIG

 

⑤. chaeck scriptより、stackへ新たに<public key hash>を追加(図 7 ⑤)

chaeck scriptの状態:  OP_EQUALVERIFY OP_CHECKSIG

 

⑥. ハッシュ関数によって作成された<public key hash>と、script_pubkeyに元々あった<public key hash>を比較(図 7 ⑥)

chaeck scriptの状態:  OP_CHECKSIG

 

⑦. OP_CHECKSIGによって、script全体の検証を行い、有効であれば ”1” を返す(図 7 ⑦)

ここで、”1(true)”がstackに残っていれば、署名検証が成功したと見なされ、stackに”0(false)や2つ以上のdataが残った場合や、stackが空だった場合”、署名検証が失敗したと見なされます。

chaeck scriptの状態: なし

 

図 7

 

この様な手順を追って、BitcoinはscriptのOP_codeを実行して、transactionのutxoにデジタル署名の検証を行なっています。

今回は、P2PKH(Pay to Public Key Hash)と呼ばれるscriptを見てきましたが、他にも、multi signatureのscriptやP2SH(Pay to Script Hash)のscriptなど、複数種類があります。大まかな違いは、multi signatureであれば、<signature>と<public key>が複数個になる事(n of mのnとmの数で個数が前後します)や、P2SHでは、multi signatureのscriptでは、scriptのbyte数が大きくなってしまう問題から、redeem scriptと呼ばれるscriptのある部分のハッシュを持たせるなど、scriptの使用が異なります。考え方は、今説明したP2PKHのscriptとほぼ同じです。

 

segwitを理解する

script_sigには<signature> と <public key>の2つの情報が入っていました。

 

<signature>は、秘密鍵によって署名された署名情報が入っています。 ここで、もう一度、下図のtransactionの構造をみて下さい。

 

図 8

 

transactoinに署名をするというのは、図 8の水色の部分である署名対象データのハッシュ値を生成し、そのハッシュ値に秘密鍵で署名を実行し、その署名データをscript_sigにセットすることを言います。ここの記事で言う所の、<signature>ですね。しかし、署名データがセットされたscript_sigには署名する事が出来ないので、ハッカーにとってみたら、ここだけが唯一、transactoinのdata構造で改ざんをする事が出来るポイントになります。script_sig以外の部分で改ざんをしてしてしまうと、署名検証時エラーになってしまうので、transactionを改ざんすることが出来ません。

 

しかしながら、transactionのidであるtx_idは、script_sigも含めた図. 8のtransactionの全データから生成されたハッシュ値であり、もしscript_sigをハッカーに改ざんされてしまった場合、tx_idの値も別の値になってしまいます。つまり、署名検証に影響を与えない様にscript_sigを改ざんすれば、tx_idの異なるtransactionを作成する事が出来ます。これがいわゆる、transactionのmalleabilityと呼ばれるものです。

 

このtransactionのmalleability問題を解決しようと実装されたのが、segregated witness(segwit)です。segwitでは、transactionのinputからscript_sigを分離し、witnessという別のデータ領域に移動する事でtransactionのmalleability問題を解決します。

図 9

 

segwitのtransactionは、署名データがinputから分離され、tx_idの算出にscript_sigを使用しなくなるので、script_sigを改ざんによるtransactionのmalleabilityを解決し、未署名なtransactionでもtx_idを算出できる様になります。

 

segwitは未署名な状態でもtransactionのtx_idが算出する事が出来るので、未署名なtransactionをinputにした、transactionのチェーンを作る事ができます(厳密にはオフチェーン上でtransactionのやり取りを行うのですが、話が長くなるので別途記事を書く関係から、この様に表現しています)。この、未署名なtransactionのtx_idを算出する事が出来ると言う技術が、Lightning Networkpayment channelの部分で利用されていますpayment channelでは、transctionのやり取りをする為にチャネルをオープンにする必要があり、チャネルをオープンにする為には、未署名であるtransactionのtx_idが必要になります。

 

payment channelやLightning Networkについては、後日にブログを書きます。

 

 

この記事のまとめ

  • transactionのデジタル署名の検証には、op_codeを並べたscriptが使用されている
  • transactionのscript_sigの部分には署名出来ないので、transactionのmalleability問題が起る
  • transactionのmalleability問題を解決する為に開発されたのが、segwitである
  • segwitの未署名でもtx_idを作成できるよ、という特性がpayment channelやLightning Networkの技術基盤になっている

 

参考: ブロックチェーン・プログラミング 仮想通貨入門(著者: 山崎重一郎、 安土茂亨、 田中俊太郎)

 

コインチェックでは様々なポジションでメンバーを募集中です。まずは話を聞いてみたい、という方も下記のリンクから是非ご応募ください。

 

corporate.coincheck.com