はじめに
この記事は、暗号通貨 Advent Calendar 20日目の記事です。Bitcoinのトランザクションには欠かせない、ロジックの部分を担っているScriptについて解説します。プログラミングや初歩的な数学の知識、ビットコインの概要の理解を前提としています。この記事を読んで、最も典型的なP2PKHのScriptを理解できるようになることが目標です。私は情報学の専門ではないので、間違いがあったらコメントや編集リクエストで指摘していただけるとありがたいです。
Transactionの構造を理解している前提の記事なので、Mastering Bitcoinのトランザクションの章やこの記事を事前に読むことをおすすめします。
Scriptとは
Scriptとは、Bitcoinのトランザクション内に書かれるプログラミング言語の一種です(チューリング完全ではないのでプログラミング言語といってよいのかは微妙)。左から右に向かって処理され、LIFO (Last In First Out)と呼ばれるデータ構造を持ちます。例えば、普通のプログラミング言語だと、
5 + 2
というように書かれますが、実際の手順は
- メモリに5を用意する
- メモリに2を用意する
- 足し算をして結果をメモリに格納する
のように、5→2→+ と言う順番で処理されます。一方、Scriptは
OP_5 OP_2 OP_ADD
のように書かれ、手順は先程と同様なので、コードの左から右に向かって処理されることがわかります。
Bitcoinにおいては、Scriptによって誰が、どのような条件でそのBitcoinを使用できるかということを示しています。
実際のScriptを見てみる
習うより慣れろということで、実際に最もよく使われているP2PKH(pay-to-public-key-hash)トランザクションのScriptを見てみましょう。
scriptPubKey: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
scriptSig: <sig> <pubKey>
2行に渡って書かれていますが、上の行がトランザクションのOutputに、下の行がInputに書かれます(InputとOutputがわからない人はWikiとかその日本語版とか僕が以前書いた記事を読んで下さい)。OutputにはBitcoinをロックするロジックが、Inputにはそれをアンロックするために必要なものが書かれています。
OP_DUPとかOPなんちゃらって何。。。って感じですよね。ってことで以下に説明します。
Opcode
scriptPubKeyにある「OP_」ではじまる文字列のことを、オペコード(opcode)と呼びます。一般的なオペコードについて詳しくは知らないのですが、一つ前のスタックに対して処理のかたまりを実行するもの、なので関数とかメソッドに近いものだと理解しています(違ったら教えてください)。
それ以外にも、定数を表すオペコードもあったりします。以下に、種類ごとにオペコードを抜粋したものを載せます。
定数
word | opcode | hex | input | output | Description |
---|---|---|---|---|---|
OP_0, OP_FALSE | 0 | 0x00 | Nothing. | (empty value) | An empty array of bytes is pushed onto the stack. (This is not a no-op: an item is added to the stack.) |
N/A | 1-75 | 0x01-0x4b | (special) | data | The next ''opcode'' bytes is data to be pushed onto the stack |
OP_PUSHDATA1 | 76 | 0x4c | (special) | data | The next byte contains the number of bytes to be pushed onto the stack. |
OP_PUSHDATA2 | 77 | 0x4d | (special) | data | The next two bytes contain the number of bytes to be pushed onto the stack. |
OP_PUSHDATA4 | 78 | 0x4e | (special) | data | The next four bytes contain the number of bytes to be pushed onto the stack. |
OP_1NEGATE | 79 | 0x4f | Nothing. | -1 | The number -1 is pushed onto the stack. |
OP_1, OP_TRUE | 81 | 0x51 | Nothing. | 1 | The number 1 is pushed onto the stack. |
OP_2-OP_16 | 82-96 | 0x52-0x60 | Nothing. | 2-16 | The number in the word name (2-16) is pushed onto the stack. |
スタック
word | opcode | hex | input | output | Description |
---|---|---|---|---|---|
OP_DUP | 118 | 0x76 | x | x x | Duplicates the top stack item. |
OP_SWAP | 124 | 0x7c | x1 x2 | x2 x1 | The top two items on the stack are swapped. |
OP_2DUP | 110 | 0x6e | x1 x2 | x1 x2 x1 x2 | Duplicates the top two stack items. |
OP_3DUP | 111 | 0x6f | x1 x2 x3 | x1 x2 x3 x1 x2 x3 | Duplicates the top three stack items. |
OP_2SWAP | 114 | 0x72 | x1 x2 x3 x4 | x3 x4 x1 x2 | Swaps the top two pairs of items. |
暗号
word | opcode | hex | input | output | Description |
---|---|---|---|---|---|
OP_RIPEMD160 | 166 | 0xa6 | in | hash | The input is hashed using RIPEMD-160. |
OP_SHA1 | 167 | 0xa7 | in | hash | The input is hashed using SHA-1. |
OP_SHA256 | 168 | 0xa8 | in | hash | The input is hashed using SHA-256. |
OP_HASH160 | 169 | 0xa9 | in | hash | The input is hashed twice: first with SHA-256 and then with RIPEMD-160. |
OP_HASH256 | 170 | 0xaa | in | hash | The input is hashed two times with SHA-256. |
[[OP_CHECKSIG]] | 172 | 0xac | sig pubkey | True / false | The entire transaction's outputs, inputs, and script (from the most recently-executed OP_CODESEPARATOR to the end) are hashed. The signature used by OP_CHECKSIG must be a valid signature for this hash and public key. If it is, 1 is returned, 0 otherwise. |
ビット演算
word | opcode | hex | input | output | Description |
---|---|---|---|---|---|
OP_EQUAL | 135 | 0x87 | x1 x2 | True / false | Returns 1 if the inputs are exactly equal, 0 otherwise. |
OP_EQUALVERIFY | 136 | 0x88 | x1 x2 | Nothing / ''fail'' | Same as OP_EQUAL, but runs OP_VERIFY afterward. |
P2PKHスクリプトを詳しく見てみる
pay-to-public-key-hash のスクリプトはこんなんでした。
scriptPubKey: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
scriptSig: <sig> <pubKey>
transactionを作成するときは、以前自分に送金された未使用のtransaction output を参照するのでした。そこにはscriptPubKeyが書かれており、適当な電子署名(sig)と公開鍵(pubkey)を与えてあげればoutputがアンロックされて、Bitcoinを送金できるというわけです。
さて、実際にアンロックされる過程を見ていきます。
手順1.
<sig> <pubKey> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
utxo(unspent transaction output)のscriptPubKeyとscriptSigを連結。電子署名と公開鍵を使ってアンロックする準備。この連結したものを、左から順番にスタックに移動していき、実行していく。
手順2.
<sig> <pubKey>
2つの定数をスタックに移動。
手順3.
<sig> <pubKey> <pubKey>
手順2のOP_DUPに右から OP_DUPを作用させた。表のスタックの部分を見ると、「スタックの一番topのアイテムを複製する」と書いてある。topアイテムであるpubKeyが複製されている。
手順4.
<sig> <pubKey> <pubHashA>
OP_HASH160を右から作用させた。pubKeyのハッシュ値を計算した。
手順5.
<sig> <pubKey> <pubHashA> <pubKeyHash>
もともとアウトプットにあったpubKeyHash(ビットコインアドレスに当たる)を右に追加した。
手順6.
<sig> <pubKey>
pubKeyから計算されたpubHashA(計算結果)と、scriptPubKey にあったpubHash(答え)が等しいことが検証された(その後はスタックには残らない)。
手順7.
1
署名がチェックされた。正しかったので、1(true)が返された。
ここでtrueが返されたので、このtransactionは有効となります(マイナーによってチェックされた後、ブロックに取り込まれる)。
どうでしょう、オペコードの意味を知って、ひとつひとつ丁寧に見ていくと、意外とシンプルなことがわかると思います。
理解できなかった方も、わかりやすい参考リンクを最後に貼ったので、読んでみてください。Davide De
Rosaは特に詳しく、系統的に書かれいるのでおすすめです。
練習問題
bitFlyerさんの採用ページに手計算で解ける練習問題があるので、解いてみるとScriptをより理解できるかもしれません。この問題は、結局は3変数の連立一次方程式になります。シンプル。注意点としては、解答は16進数に変換して入力する必要があります。
現在はページが変わっています。
参考
- Script - Bitcoin Wiki
- Davide De Rosa(わかりやすくてオススメ)
- Script | Bitcore