暗号通貨のトランザクションを手作りする (6)

今回のおはなし

みなさんこんにちは。

VIPPOOL でエンジニアをやっています、星月です。

前回は秘密鍵と公開鍵のペアを作って、公開鍵のハッシュ値を求めました。
今回は電子署名を作ってみましょう。

OP_CHECKSIG の仕様

電子署名を作るには、OP_CHECKSIG の仕様を知る必要があります。

...とはいえ、実際に使われているパターンは1つしかないので、
それだけ覚えてもらえれば十分です。

bitcoin wiki の OP_CHECKSIG
ページに図と共に詳細な説明がありますが、もう少し噛み砕いて説明します。

  1. まず、UTXO の scriptPubKey を取り出します。
  2. 新規トランザクションの scriptPubKey を設計します。
  3. 次に、新規トランザクションの scriptSig を全部空っぽ、長さは 0 にしたデータを作ります。
  4. 電子署名を作る対象の入力データだけ、scriptSig の部分に、取り出した scriptPubKey を挿入します。
  5. この状態のデータの SHA256 ハッシュを2度計算して、電子署名を作成します。

具体的には図のように作ります。

f:id:y-hoshizuki:20181022130426p:plain

電子署名は scriptSig ごとに作る必要があるため、
入力が複数ある場合はこのようになります。

f:id:y-hoshizuki:20181022130435p:plain

さて、それでは実際に作ってみましょう。
今回は monacoin のテストネットで実験してみます。
自分から自分に送る単純なトランザクションです。

UTXO の scriptPubKey

P2PKH を使っているなら以下の形式で固定です。

76   ... OP_DUP
A9   ... OP_HASH160
14   ... 公開鍵のハッシュの長さ (PUSH 命令)
DB 2E 05 4B B5 76 DC 63 28 3C 00 60 AB D4 6D F1 40 57 C2 DE ... 公開鍵のハッシュ
88   ... OP_EQUALVERIFY
AC   ... OP_CHECKSIG

長さは 25Byte です。

新規トランザクションの scriptPubKey

P2PKH で出力するなら先ほどと同じです。

76   ... OP_DUP
A9   ... OP_HASH160
14   ... 公開鍵のハッシュの長さ (PUSH 命令)
DB 2E 05 4B B5 76 DC 63 28 3C 00 60 AB D4 6D F1 40 57 C2 DE ... 公開鍵のハッシュ
88   ... OP_EQUALVERIFY
AC   ... OP_CHECKSIG

長さも同じく 25Byte です。

新規トランザクションの準備

第3回で説明した通りに scriptSig を空っぽの状態で raw トランザクションを作ります。

02 00 00 00      ... バージョン 2
01               ... 入力は 1 つ

入力データ
 82 82 9E 2C 9E F9 81 BA 16 63 42 86 AB 9C 6F F2
 1B E0 44 AE BA FB 77 30 81 F5 EE 31 46 B0 7A C3
                  ... 前 TXID: c37ab04631eef5813077fbbaae44e01bf26f9cab86426316ba81f99e2c9e8282
 00 00 00 00      ... 前 TX の第0出力を使用する
 00               ... scriptSig は 0 Byte
 00 00 00 00      ... シーケンス番号はとりあえず 0

01               ... 出力は 1 つ

出力データ
 C0 9E E6 05 00 00 00 00 ... 0.99 tMONA
 19               ... scriptPubKey は 25 Byte
 76 A9 14
 DB 2E 05 4B B5 76 DC 63 28 3C 00 60 AB D4 6D F1 40 57 C2 DE
 88 AC            ... scriptPubKey 本体

00 00 00 00      ... ロックタイムはとりあえず 0

電子署名対象となる入力だけ、scriptSig を埋める

同じなので区別がつきづらいですが、
トランザクションから抽出した scriptPubKey を、
scriptSig を埋めるべき場所に配置します。

そして末尾に 01 00 00 00 をくっつけます。

02 00 00 00      ... バージョン 2
01               ... 入力は 1 つ

入力データ
 82 82 9E 2C 9E F9 81 BA 16 63 42 86 AB 9C 6F F2
 1B E0 44 AE BA FB 77 30 81 F5 EE 31 46 B0 7A C3
                  ... 前 TXID: c37ab04631eef5813077fbbaae44e01bf26f9cab86426316ba81f99e2c9e8282
 00 00 00 00      ... 前 TX の第0出力を使用する
 19               ... scriptSig ではなく前トランザクションの scriptPubKey の長さは 25 Byte
 76 A9 14
 DB 2E 05 4B B5 76 DC 63 28 3C 00 60 AB D4 6D F1 40 57 C2 DE
 88 AC            ... 前トランザクションの scriptPubKey 本体
 00 00 00 00      ... シーケンス番号はとりあえず 0

01               ... 出力は 1 つ

出力データ
 C0 9E E6 05 00 00 00 00 ... 0.99 tMONA
 19               ... scriptPubKey は 25 Byte
 76 A9 14
 DB 2E 05 4B B5 76 DC 63 28 3C 00 60 AB D4 6D F1 40 57 C2 DE
 88 AC            ... scriptPubKey 本体

00 00 00 00      ... ロックタイムはとりあえず 0

01 00 00 00      ... くっつける

SHA256 ハッシュを2度計算して電子署名を作成する

うまく整形してバイナリ形式で openssl に流し込みます。
openssl dgst で1回 sha256 を計算するので、先に一度 sha256 を計算しておく必要があることに注意してください。

$ printf "%b" "\x02\x00\x00\x00\x01\x82\x82\x9E\x2C\x9E\xF9\x81\xBA\x16\x63\x42\x86\xAB\x9C\x6F\xF2\x1B\xE0\x44\xAE\xBA\xFB\x77\x30\x81\xF5\xEE\x31\x46\xB0\x7A\xC3\x00\x00\x00\x00\x19\x76\xA9\x14\xDB\x2E\x05\x4B\xB5\x76\xDC\x63\x28\x3C\x00\x60\xAB\xD4\x6D\xF1\x40\x57\xC2\xDE\x88\xAC\x00\x00\x00\x00\x01\xC0\x9E\xE6\x05\x00\x00\x00\x00\x19\x76\xA9\x14\xDB\x2E\x05\x4B\xB5\x76\xDC\x63\x28\x3C\x00\x60\xAB\xD4\x6D\xF1\x40\x57\xC2\xDE\x88\xAC\x00\x00\x00\x00\x01\x00\x00\x00" | openssl sha -sha256 -binary | openssl dgst -sha256 -sign wallet.pem -hex
(stdin)= 304402205c66d2c6f1ae9d91d4103f4f40b726cc9c272142b1a17fceb5237445fdbd9563022021f12c9da77eac953531616ee673dc1969a048c3c16aeb6022e0d2e8cad3e64f

これで、電子署名は完成です。

電子署名は乱数を使用するため毎回異なる署名が出力されます。
今は詳しく説明しませんが、正規化ルールの都合があるので、
何度か実行して 3044 から始まるものを選んでください。

今回はここまで。
ご質問、ご意見等ありましたらお気軽にリプライください。