Blockchain in Go のメモ
by mmyoji
5 min read
Blockchain いい加減ちゃんと勉強しとくかということで、以前ちらっと目に入った Building Blockchain in Go. Part 1: Basic Prototype · Going the distance をやった。
前提知識
これをいきなりやったわけではなく、 Blockchain に関してはいくつか記事を読んだ。
最低限仕入れた知識としては、
- BTC の文脈で言う Blockchain の Block は transaction 情報及び、その前の transaction の Hash 値を持ったもの
- それが取引毎に連なっていく
- 1つの取引を改竄した場合、それに連なる Block すべての Hash 値が変わっていくため不正がしづらい
ぐらい。非常にシンプル。
Proof of Work という単語が出たがあまりわかってない。
あと Blockchain の用途としては、 Bitcoin の transaction 以外にも、 identification などに使えそうってくらい。
食品の流通経路を Blockchain で管理・公開して安全性を保証したり、絵画が偽物じゃないことを保証したり。
詳しくは ブロックチェーンを応用した革新的サービス13選 | Tech2GO を。
以下先に挙げた記事に関するメモを残しておく。
Part1
Block, Blockchain 構造体の定義。
実際はもっと複雑な struct だけど、ここでは話を単純にするためにシンプルにしておくよ、とのこと。
Block
- Timestamp: Block が生成された時点の timestamp
- Data: BTC だとここに Transaction 情報が入ったりする。BTC に限定しているわけではないので雑に Data となっている。
- PrevBlockHash: 前の Block の Hash 値
- Hash: 自 Block の Hash 値。 Timestamp, Data, PrevBlockHash すべてを使って Hash 値を出す
Blockchain
単に Block の(ポインタの)配列。 Golang だと配列は ordered になっているのでとりあえず配列にしてる。
初期 Block は GenesisBlock と呼ぶらしい。 PrevBlockHash は当然持たない。
Part2
Proof-of-Work
adding new blocks is a hard work
新しい Block を追加するには新しい Hash を探す必要がある。それがマシンパワーを使って大変。
これが Work
Proof 自体は簡単
ここでの説明は抽象的過ぎてよくわからなかった。
ビットコインのProof of Workシステムとは何か - Genx Notes
【基礎】ビットコイン(bitcoin)のProof of Work(プルーフオブワーク)をわかりやすく解説 | moblock
ここらへんを読むともう少しわかるかも。
新しいブロック追加にはコストがかかり、これによって不正を防ぐ。というのが大事らしい
上記リンクには PoW の欠点など(51%問題)も書かれているがここでは省略する。
Hashcash
Hash 値を求める方法、っていう理解
特定のデータ + counter
(初期値は 0) の組み合わせの Hash
値を計算して、それが条件に合うまで counter
を increment -> Hash
値計算をしていく
条件の例としては「 Hash 値の最初の 20bits が0」のようなもの。
Bitcoin (もしくは Blockchain) の文脈では counter
を nonce
と呼ぶみたい。
Implementation
サンプルコード実行はそこそこ時間がかかって(多分10~15分くらい)、「これが PoW か...」と少し実感できた。
Part3
データの永続化と CLI 実装について
実際は分散システムだが、今回も単純化のためにそこは省略
Database Choice
なんでもいいけど、 Bitcoin では LevelDB が使われてる。
今回は Go で書かれてることもあり、 BoltDB を使う。
雑に説明すると BoltDB は Go 製でサーバーを起動する必要ない、最小限の機能を備えた KVS
昔ちょこっと試してみたことあるけど、割とよさげだった。詳しくは GitHub を見て
BoltDB の key と value はただの []byte
なので、 encoding/gob
で Go
の構造体を serialize して格納する。
今回は gob 使うけど、別に JSON, XML, Protocol Buffer でもいいよ、とのこと
Database Structure
Bitconin Core では 2 つの buckets が使われてる
blocks
chain 内のすべての block の metadatachainstate
文字通り
Bitconin Core だとさらに各 bucket 毎に色々な rule で key-value を保存しているが、ここでも簡略化してある
Part4
Transaction について
Bitcoin Transaction
複数の入力、複数の出力がある。
入力は前の Transaction の出力。出力は「どこにコインが格納されるか」
Implmentation
この part では、誰から誰にいくら送ったかを記録できる transaction を実装した。
送金元や送金先 (address) はただの string で人の名前を直接指定するだけ。
1 Block に対して 1 transaction しか持てない実装
Part5
先の cheap な address ではなく、実際に bitcoin に実装されてるような address を実装してゆく〜
- address は公開されている
- identity は公開鍵 public key(pub-key) と秘密鍵 private key(prv-key) のペア
- pub-key は human-readable な形で表示される
- この鍵(実際は private key の方)に物理的に?アクセスされない限りは自分の coin が利用されることはない
- prv-key で署名をして、 pub-key で検証をする
- 署名するには「署名のためのデータ」「prv-key」が
- 検証には「署名されたデータ」「署名」「pub-key」が必要
- 生成された署名は Transaction の input に格納される
- 署名は暗号ではないので、署名から元のデータを生成することはできない
- 可逆暗号ではないってことかな?
- Block に入る前に Transaction の検証が走る
- TXInput が前の TXOutput を使う権限があるかどうか
- 署名が正しいかどうか
Elliptic Curve Cryptography
他人の prv-key をたまたま生成できる、なんてことがあってはいけないので Bitcoin では prv-key の生成に Elliptic Curve というアルゴリズムを使っている。
詳細は省略
Bitcoin では transaction の署名に ECDSA(Elliptic Curve Digital Signature Algorithm) ってやつを使ってるらしい。
Base58
pub-key を human-readable にするために Base58 を使ってる。
アルゴリズム自体は Base64 に似てるけど、より短い?アルファベットしか使ってない。
紛らわしい 0, o, i, l なんかは使ってない。 + や / も使ってない
Bitcoin address 生成自体はもうすこし複雑。詳細はもとの記事を見ろ
Impl
Wallet
構造体は PublicKey と PrivateKey を持つ- prv-key -> pub-key の順に生成される
- address の生成は複雑(ということだけ覚えておけばいいでしょ)
- ここらへんの値を使って TXInput, TXOutput あたりを修正
- ScriptPubKey -> PubKeyHash
- ScriptSig -> Signature, PubKey
blog にはほんの一部しかコードが書いてないため、基本的に GitHub から写経するしかなく、結構つらかった。
Impl part が2つに分かれているが、 GitHub だけだとどこまで実装すれば前半パートが達成されるか把握しづらく結局ほぼすべて写経した
結構作業なので、概念理解だけでいいなら写経はしなくてもよさそう
Part6~
ここまでやって、 Bitcoin Core の話が頻繁に出てきて、当初の目的(Blockchain の理解をする)から少し逸れている感じがしてきたのでやめる。
決して写経がめんどくさくなったわけではない。本質厨なので
まとめ
写経しなくてよかったかも