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

今回のおはなし

みなさんこんにちは。

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

前回は P2PKH の動作原理について解説しました。
それでは実際にテンプレートを埋める方法について説明していきます。

各要素とコインアドレスの関係

まずは各要素の関係について確認しておきましょう。

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

基本的に一方通行ですが、コインアドレスと公開鍵のハッシュだけ、
相互に変換可能なところに注意してください。

秘密鍵と公開鍵のペアを作成する

openssl を使うと簡単に作れます。
secp256k1 という名前の曲線を使いますが、比較的新しいバージョンの openssl でないと
使えないので注意してください。

$ openssl ecparam -genkey -name secp256k1 -out wallet.pem
$ openssl ec -in wallet.pem -conv_form compressed -text
Private-Key: (256 bit)
priv:
    00:8d:6a:af:b3:37:d6:e3:dd:5a:96:c1:54:95:c6:
    cd:4b:65:1e:52:d0:82:e3:e2:a7:89:28:35:bb:94:
    9c:e1:07
pub:
    02:b4:ee:a1:4b:61:45:a8:97:f8:75:61:3c:80:fe:
    3b:3f:50:d4:69:a5:b2:88:6b:50:5f:55:f6:a3:08:
    b9:e8:74

priv が秘密鍵 256bit、pub が compressed 形式の公開鍵です。

公開鍵のハッシュを求める

次は公開鍵のハッシュを求めます。
公開鍵の : を \x に置換して繋げてから、
openssl で sha256, ripemd160 の順でハッシュを計算します。

$ printf "%b" "\x02\xb4\xee\xa1\x4b\x61\x45\xa8\x97\xf8\x75\x61\x3c\x80\xfe\x3b\x3f\x50\xd4\x69\xa5\xb2\x88\x6b\x50\x5f\x55\xf6\xa3\x08\xb9\xe8\x74" | openssl sha -sha256 -binary | openssl sha -ripemd160
(stdin)= db2e054bb576dc63283c0060abd46df14057c2de

これが公開鍵のハッシュです。

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

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

今回のおはなし

みなさんこんにちは。

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

前回までで簡単な raw トランザクションの手作りができるようになりました。
今回からは本格的に、実際に使える raw トランザクションを作る方法について解説していきます。

前回までの解説で抜けているところといえば、Script で使われる P2PKH についてです。
あとは P2PKH のスクリプトを書けるようになれば、raw トランザクションについては完璧です。

P2PKH の仕組み

第2回ではさらっと流しましたが、もう一度、P2PKH は以下の形式のプログラムです。

scriptSig:
 PUSH 電子署名
 PUSH 公開鍵
scriptPubKey:
 OP_DUP
 OP_HASH160
 PUSH 公開鍵のハッシュ値
 OP_EQUALVERIFY
 OP_CHECKSIG

ここで、「電子署名」「公開鍵」「公開鍵のハッシュ値」の埋め方がわかれば、
このテンプレートどおりに書くだけで OK です。

ですが、先にこのプログラムの動作原理を理解しておきましょう。

まず、scriptSig を実行し終わった段階で、スタックは以下の状態になっています。

公開鍵   <- スタックトップ
電子署名

次に OP_DUP はスタックトップをコピーします。

公開鍵   <- スタックトップ
公開鍵
電子署名

OP_HASH160 は、スタックトップの SHA-256 ハッシュを計算してから、RIPEMD-160 ハッシュを計算します。

RIPEMD165(SHA256(公開鍵)) <- スタックトップ
公開鍵
電子署名

さらに公開鍵のハッシュ値を PUSH します。

公開鍵のハッシュ値 <- スタックトップ
RIPEMD165(SHA256(公開鍵))
公開鍵
電子署名

次の OP_EQUALVERIFY は、OP_EQUAL と OP_VERIFY をまとめて実行する命令で、
要するにスタックの先頭2つが一致しなければプログラムの実行を FAIL させる命令です。
つまり、「公開鍵のハッシュ値=RIPEMD165(SHA256(公開鍵))」が、続きを実行する条件です。
実行後はスタックの先頭2つが消えます。

公開鍵 <- スタックトップ
電子署名

最後に OP_CHECKSIG は、スタックに積まれた公開鍵と電子署名を使って、
「正しい持ち主が持つ秘密鍵によって作られたトランザクションである」ことを確認します。

成功したら 1 True, 失敗したら 0 False <- スタックトップ

要するに

  1. 公開鍵のハッシュ値=RIPEMD165(SHA256(公開鍵)) であること
  2. トランザクションデータが正しい秘密鍵によって電子署名を付与されていること

の2点を確認するプログラムとなっているわけです。

今回はここまで。
次回からはテンプレートの埋め方について解説していきます。
ご質問、ご意見等ありましたらお気軽にリプライください。

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

今回のおはなし

みなさんこんにちは。

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

前回までで、トランザクションの概念と Script について解説しました。

今回は raw トランザクションの構造について説明します。
これで簡単なトランザクションが手作りできるようになります。

raw トランザクションとは、トランザクションの実データのことで、
可変長のバイナリ列で表現されます。
今回はこれを手作りすることを目指します。

var_int 型

トランザクションのデータで、数値を格納するために多用されている形式です。

格納したい値 x を、以下のルールで可変長のバイナリ列にします。

値域 形式
0≦x<0xFD x をそのまま 1 Byte で記述
0xFD≦x≦0xFFFF 0xFD に続けて x を 2 Byte リトルエンディアンで記述
0x10000≦x≦0xFFFFFFFF 0xFE に続けて x を 4 Byte リトルエンディアンで記述
0x100000000≦x 0xFF に続けて x を 8 Byte リトルエンディアンで記述

var_int に限らず、raw トランザクションではすべてリトルエンディアンを使用します。

トランザクションの構造

トランザクションの構造自体は単純で、ヘッダ+入力データ+出力データの構成となっています。

内容
バージョン (1か2を入れておく) int32_t
入力の個数 var_int
入力データの配列 後述
出力の個数 var_int
出力のデータ配列 後述
ロックタイム uint32_t

ロックタイムというのが初出ですが、ここは通常 0 を入れておけば問題ありません。

ざっくりこんなイメージです。

02 00 00 00      ... バージョン 2
03               ... 入力は 3 つ
入力データ1
入力データ2
入力データ3
02               ... 出力は 2 つ
出力データ1
出力データ2
00 00 00 00      ... ロックタイムはとりあえず 0

トランザクションの入力データ

トランザクションの入力データは以下の形式です。

内容
TXID uint256_t
出力インデックス uint32_t
scriptSig の長さ var_int
scriptSig 本体 可変長バイナリ
シーケンス番号 uint32_t

TXID はトランザクション ID のことで、256bit の整数をリトルエンディアンで書きます。

出力インデックスは、入力として接続する相手のトランザクションの出力の、
何番目か、というインデックスです。

シーケンス番号は普段使うことはないので 0 で問題ありません。

例えばこんな接続の場合。

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

scriptSig には以下のプログラムを含めるとします。

02 CD AB         ... OP_PUSH 0xABCD

こうなります。

EC F2 65 AF 3E 30 B4 44 BC 14 1E EB 95 60 DD E8
34 DD 1B E2 CD 5D 2D BE 2F 84 75 01 00 A8 02 3F
                 ... 前 TXID: 3f02a8000175842fbe2d5dcde21bdd34e8dd6095eb1e14bc44b4303eaf65f2ec
02 00 00 00      ... 前 TX の第2出力を接続する
03               ... scriptSig の長さ
02 CD AB         ... scriptSig 本体
00 00 00 00      ... シーケンス番号はとりあえず 0

入力が複数ある場合は、この形式を連続して並べます。

トランザクションの出力データ

トランザクションの出力データは以下の形式です。

内容
出力金額 int64_t
scriptPubKey の長さ var_int
scriptPubKey 本体 可変長バイナリ

出力金額はコインではなく satoshi 単位の整数で書きます。
100,000,000 satoshi = 1.0 BTC です。
MONA の場合は特に呼び名は決まっていないそうですが、
同じ単位で書きます。

scriptPubKey には以下のプログラムを含めるとします。

02 CD AB         ... OP_PUSH 0xABCD
87               ... OP_EQUAL

こうなります。

00 E1 F5 05 00 00 00 00 ... 1BTC (100000000 satoshi)
04               ... scriptPubKey の長さ
02 CD AB 87      ... scriptPubKey

出力が複数ある場合は、この形式を連続して並べます。

まとめ

いかがでしたでしょうか。
raw トランザクション、意外に簡単な構造ではないでしょうか。

既にお気づきの方もいるとは思いますが、
TXID をプライマリキーとして見ると、
トランザクションのリンクは過去に向かう単方向リストの構造になっています。

よくよく考えれば当然の仕様で、
ブロックチェーンは追記はできても変更はできない分散型データベースですので、
トランザクションのリンクも必然的に過去に向かう単方向リストにせざるを得ないんですね。

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

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

今回のおはなし

みなさんこんにちは。

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

前回はトランザクションとはそもそも何か?というお話をして、
UTXO と残高の関係について説明しました。

そのときに、「UTXO は通常ロックされている」「アンロックする権利こそが所有権の実体」
というお話をしました。

このロック、アンロックは実際どのように実現されているのでしょうか、
というのが今回のテーマです。

scriptPubKey と scriptSig

bitcoin やそこからフォークした各種アルトコインでは、ロック・アンロックは、
共通の「Script」と呼ばれる言語で記述されたプログラムコードで実現されています。

このプログラムは、スタック型のバーチャルマシンで実行されて、
最終的な結果が 1 True となればアンロックに成功して、次のトランザクションの入力に接続できます。

...と説明しても抽象的でわかりづらいですね。
具体例を挙げていきます。

トランザクションの出力には scriptPubKey という名前のプログラムが記述されています。
例えばこんな感じ。

OP_PUSH 0xABCD
OP_EQUAL

OP_PUSH は続く値をスタックにプッシュします。
OP_EQUAL はスタック先頭の2つの値を比較して、
異なる場合にはスタックに 0 False を、一致する場合には 1 True を積みます。

次に接続しようするトランザクションの入力には、scriptSig という名前のプログラムを記述します。
例えばこんな感じ。

OP_PUSH 0xABCD

暗号通貨のコインノードは、この出力と入力を接続しても良いか判断するために、
2つのプログラムを連結して実行します。

OP_PUSH 0xABCD
OP_PUSH 0xABCD
OP_EQUAL

0xABCD を2個積んで比較するので、実行後はスタックトップに 1 True が積まれた状態になります。
これでこの2つのトランザクションは接続しても問題ないと判断され、ブロックチェーンに組み込まれます。

図にするとこんな感じです。

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

まるで、scriptPubKey にアンロックの条件が記述されていて、
scriptSig に対応するプログラムを与えることでアンロックに成功する、
というような解釈ができるのではないでしょうか。

さて、では違う scriptSig だとどうでしょう?

OP_PUSH 0xDEADBEAF

コインノードは2つのプログラムを連結して実行します。

OP_PUSH 0xDEADBEAF
OP_PUSH 0xABCD
OP_EQUAL

スタックには異なる値が載っているので、OP_EQUAL の実行後、スタックトップには 0 False が積まれます。
このような場合は不正なトランザクションとして後続のトランザクション自体を拒絶します。

正しい scriptSig でなければ接続を拒絶する、まさにロックされている状態といえるでしょう?

暗号通貨ではこのような仕組みで、ロック・アンロックを実現しています。

今回は OP_PUSH と OP_EQUAL の2命令だけを使った簡単なプログラムですが、
他にもいろいろな命令が使えます。
bitcoin wiki の Script に命令の一覧があるので、
さらっと眺めてみるのもいいかも知れません。

よく使われる Script プログラム

Script 言語で記述可能な範囲であればどんなアンロック条件でも書くことができますが、
一般的に使われるプログラムは形式が決まっています。

今現在、最もよく使われているのが、P2PKH と呼ばれる形式のプログラムで、
以下の形式で記述されます。

scriptSig:
 PUSH 電子署名
 PUSH 公開鍵
scriptPubKey:
 OP_DUP
 OP_HASH160
 PUSH 公開鍵のハッシュ値
 OP_EQUALVERIFY
 OP_CHECKSIG

プログラムの詳しい説明は今は避けておきますが、名前くらいは覚えていただけると幸いです。

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

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

はじめに

みなさんはじめまして。

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

技術ブログを書いて社外にアピールしよう!という目論見で
何か書くよう言われたので、とりあえず暗号通貨の根幹を成している
トランザクションについて、連載しようと思います。

最終的には raw トランザクションを手作りできるところまで解説できればと思います。

というわけで早速、今回は

  1. そもそもトランザクションとは何か?
  2. 暗号通貨の残高とは何か?
  3. UTXO のロック、アンロックについて

というところを解説していこうと思います。

今後、特に断りがない場合、単純に「暗号通貨」といった場合は
bitcoin とそこからフォークした各種アルトコインのことを指します。
Ethereum とかはまた異なる仕組みになっているのでご注意を。

暗号通貨における「トランザクション

暗号通貨の世界でトランザクションとは、
「資金の流れ」を示すデータブロックのことを指します。

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

複数の資金を一旦ひとまとめにして、それを複数に分散させます。
これがトランザクションです。

この左側がトランザクションの「入力」、右側が「出力」です。

図のようにトランザクションは複数の入力と複数の出力を持ちます。

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

このトランザクションが集まることで、図のように「資金の流れ」ができます。
このような「資金の流れ」を記述してある1単位がトランザクションというわけです。

トランザクションのことを略して TX と書いたりします。

「未使用」なトランザクション出力 UTXO

先ほどの図で、「未使用」と書いてある部分、
これはトランザクションの出力には記述されているけれども、
他のトランザクションの入力に接続されていないことを示しています。

このように、どのトランザクションの入力にも使用されていない、
けれども出力としては存在しているものを、未使用なトランザクション出力、ということで
Unspent TX Output、略して UTXO と呼びます。

UTXO は通常、「ロック」されていて、勝手に他のトランザクションの入力に
接続することはできないようになっています。

このロックを解除(アンロック)して他のトランザクションの入力に接続する権利が、
すなわち「暗号通貨の所有権」であり、その合計が「残高」となるわけです。

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