ビットコインブロックの構造
ビットコインのブロックの構造のまとめ
ブロック構造
サイズ | フィールド名 | 説明 |
---|---|---|
4バイト | Block Size | この次のフィールドからブロックの最後までのデータサイズ(バイト単位) |
80バイト | Block Header | nonceなどいくつかのフィールドがこのヘッダフィールドに含まれる |
1-9バイト | Transaction Counter | ブロックに含まれるトランザクション数 |
可変 | Transactions | ブロックに記録されるトランザクションのリスト |
ブロックヘッダ(Block Header)
サイズ | フィールド名 | 説明 |
---|---|---|
4バイト | Version | ソフトウェア・プロトコルバージョン情報 |
32バイト | Previous Block Hash | 親ブロックのハッシュ値 |
32バイト | Merkle Root | ブロックの全トランザクションに対するマークルツリーのルートハッシュ |
4バイト | TimeStamp | ブロックの生成時刻 |
4バイト | Difficulty Target | ブロック生成時のproof of workのdifficulty |
4バイト | Nonce | proof of workで用いるカウンタ |
ブロックに関してメモ
ブロック識別子となるブロックハッシュは、「ブロック"ヘッダ"」に対しSHA256アルゴリズムを用いて2回ハッシュ化した値
ブロックには、自分自身のブロックハッシュをもっていない
もう一つの識別としては、一番最初に作られたブロックを0として順番にふられた「ブロック高」
トランザクションscript
ビットコインは、locking scriptとunlocking scriptで有効性をチェックする。
locking scriptはアウトプットに置かれている解除条件で将来アウトプットを使用する際に満たさなければいけない条件
scriptは、ほぼほぼハッシュ値だが、逆ポーランド記法で書かれた計算が真ならなんでもよい
例: locking script 3 OP_ADD 5 OP_EQUAL unlocking script 2 2 3 OP_ADD 5 OP_EQUAL となるので真
標準的なscriptは、次の5つ
- P2PKH
- Pay-to-Public-Key
- マルチシグネチャ
- P2SH
- データアウトプットOP_RETURN
P2PKH
locking script OP_DUP OP_HASH160 <Cafe Public Key Hash> OP_EQUAL OP_CHCKING unlocking script <Cafe Signature> <Cafe Public Key>
つなげると
<Cafe Signature> <Cafe Public Key> OP_DUP OP_HASH160 <Cafe Public Key Hash> OP_EQUAL OP_CHCKSIG
となって真になる。なるほど。
Pay-to-Public-Key
locking script <Public Key A> OP_CHCKSIG unlocking script <Signature from Private Key A>
つなげると
<Signature from Private Key A> <Public Key A> OP_CHCKSIG
マルチシグネチャ
locking script M <Public Key 1> <Public Key 2>.... <Public Key N> N OP_CHCKMULTISIG unlocking script OP_0 <Signature B> <Signature C>
データアウトプット(OP_RETURN)
scriptにファイルの署名をおけばファイルの存在証明が可能。 そのようにこのscriptを使ってスマートコントラクトなど応用が可能ということが分かってきた。 ただ、ブロックチェーンのデータの肥大化を招くので、ビットコインでは格納できるデータは、80バイトに制限されている。
OP_RETURN
なるほど、なるほど。ここでスマートコントラクトとかの話が出てきて来るのね。
検証側は、OP_RETURNがでてきたら通貨の処理じゃないととらえて処理をやめるのか。
P2SH
マルチシグネチャなど複雑で大きいscriptを実用的にしようとしたもの。 P2SHは、「このハッシュとマッチするscriptに対して支払い、このscriptはのちほどこのアウトプットが使用するときに与えられる。」なんとなくは分かった(笑) locking scriptにscriptのハッシュを置くのね。
redeem Script マルチシグネチャなどのscript locking script OP_HASH160 <マルチシグネチャなどのscriptの20バイトのハッシュ値> OP_EQUAL unlocking script SIg1 Sig2 redeem script
結果、手数料と複雑性の負担がトランザクションの送り手から受け手に移られるのか。
pay to a script hashの名前のとおり、このハッシュを持ったscriptへのハッシュということか。
P2SHアドレス
scriptのハッシュをアドレスとして使用する。 支払ってくれる人にこのアドレスを伝えればよい。 支払う人がscriptを見ることなく支払いができるようにできる。
script難しかったけど、ここからスマートコントラクトなどいろいろな応用がきくのか。 ブロックチェーン思った以上に奥が深くて、すごいわ。
Vanityアドレス
Vanityアドレスとは、人が読めるまたは意味のある文字列を含むアドレスのこと。 例えば、
1deconstructionxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
とかです。
アドレスは、最後にハッシュ関数を使っているので、メールアドレスのようにほしいアドレスを簡単にとることはできないです。
それで、ランダムな値から鍵生成とハッシュを繰り返して、ほしい条件を満たすアドレスがでるまで待ちます(笑)
このことをvanityアドレスマイニングというみたいです。 ざっくり言えばいわゆるブロックのマイニングと同じようなものですしね。
技術的に意味あるかは微妙ですが、遊びで1deconで始まるアドレスをマイニングしてみました。
Mastering Bitcoinにあるソースをベースに最新のlibbitcoinに合わせてちょこっといじったソースが以下です。
#include <random> #include <bitcoin/bitcoin.hpp> // 以下の文字列で始まる文字列を探索 const std::string search = "1decon"; // 秘密鍵の生成. bc::ec_secret random_secret(std::default_random_engine& engine); // 秘密鍵からビットコインアドレスを算出 std::string bitcoin_address(const bc::ec_secret& secret); // 条件にあう文字列かどうか探索 bool match_found(const std::string& address); int main() { // 遊びなので疑似乱数生成器 "/dev/urandom"を使用 std::random_device random; std::default_random_engine engine(random()); // 見つかるまでループ while (true) { // ランダムな秘密鍵生成 bc::ec_secret secret = random_secret(engine); // 秘密鍵からアドレスを取得 std::string address = bitcoin_address(secret); // 取得したアドレスが条件に合うか判定 if (match_found(address)) { // 発見! std::cout << "Found vanity address! " << address << std::endl; std::cout << "Secret: " << bc::encode_base16(secret) << std::endl; return 0; } } return 0; } bc::ec_secret random_secret(std::default_random_engine& engine) { bc::ec_secret secret; // 乱数からバイト毎に並べる for (uint8_t& byte: secret) byte = engine() % std::numeric_limits<uint8_t>::max(); return secret; } std::string bitcoin_address(const bc::ec_secret& secret) { // 秘密鍵&アドレス生成 bc::wallet::ec_private private_key(secret); bc::wallet::payment_address payaddr(private_key); // エンコードして返却 return payaddr.encoded(); } bool match_found(const std::string& address) { auto addr_it = address.begin(); // 条件にあう文字列かを探索 for (auto it = search.begin(); it != search.end(); ++it, ++addr_it) if (*it != std::tolower(*addr_it)) return false; // 条件に合ったらtrue return true; }
コンパイルして実行してみると
$ sudo g++ -std=gnu++11 -o vanity-miner vanity.cpp $(pkg-config --cflags --libs libbitcoin) $ ./vanity-miner Found vanity address! 1DEcoNFKrnhwfke34iTpLjtzM2EdwT3wWT Secret: c9ccea1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
30分ほどで1DEcoNから始まるアドレスが見つかってマイニング成功! まあ、使いませんけどね(笑)
開発環境CLionとPyCharmのインストール
しばらく、ビットコインのフォーマット、構成の話が主だったので、気分転換に開発環境として C++で評判のよいCLionとPythonで評判の良いPyCharmをインストールしてみました。
CLionのインストールと起動
$ wget https://download.jetbrains.com/cpp/CLion-2018.3.4.tar.gz $ tar xvzf CLion-2018.3.4.tar.gz $ cd clion-2018.3.4/ $ sudo tar xvzf CLion-2018.3.4.tar.gz -C /opt/ $ sh /opt/clion-2083.3.4/bin/clion.sh
いくつかの文面に同意した後
無事起動!! 有料だけど、30日間は無償らしい。
ちょこっと触ったけど、結構使える。
PyCharmのインストールと起動
PyCharmのホームページから無償のLINUX community版をダウンロード
$ mkdir ~/pycharm $ mv pycham* ~/pycharm $ cd pycharm-community-xxxx.x.x $ cd bin $ ./pycharm
こっちも無事起動!
ただ、PyCharmのほうは、新しいプロジェクトを作ろうとすると落ちちゃうな。。。
(追記) メモリ不足だったらしく、使っていないバーチャルマシンを落としたら、快適に動きました。
非決定性ウォレットと決定性ウォレット、HDウォレット
ウォレットの種類をっと
ウォレットの中に秘密鍵を複数入れれるんだけど、その入っている秘密鍵同士の関係性で 大きく2つの種類のウォレットに分かれるみたい。
非決定性(nondeterministic)ウォレット
ウォレットの中の秘密鍵がランダムで、秘密鍵同士に関係性がないウォレット
関係性がないため、多く生成したとき、全部のバックアップ、頻繁なバックアップが必要になる。
決定性(deterministic)ウォレット
もととなるあるひとつのシードからハッシュ関数を使って、複数の鍵を作っていく。 シードさえバックアップをとっておけばいい。
階層的決定性ウォレット、HDウォレット
決定性ウォレットのスタンダード。最初のシードから階層的にハッシュをとって続けていく方式
マスター鍵、子鍵、孫鍵とツリー構造をとる 鍵をたくさん作っても管理しやすい、結果、匿名性を高めることができる
BIP(Bitcoin Improvement Proposal)の0032と0044では、ハッシュとしてHMAC-SHA512を使用してるっと。
Bitcoin Explorerのインストール
データの変換とか細かいことをやるには、Bitcoin Explorerがいいとのことでインストール
Bitcoin Explorerのインストール
ソースのコンパイルには、めっちゃ時間かかったので、バイナリをダウンロードがいいかと。
$ mkdir bx $ cd bx $ wget https://github.com/libbitcoin/libbitcoin-explorer/releases/download/v3.2.0/bx-linux-x64-qrcode $ mv bx-linux-x64-qrcode bx $ chmod 700 bx
ただ、結局、libbitcoinをインストールしたいというのもあり、自分の環境では、6時間かけて、、、コンパイル、インストールしました。。。
動作確認
動作確認でいろいろなフォーマット変換とかの実験
$ bx seed | bx ec-new | bx ec-to-wif #秘密鍵の作成とWIF形式への変換 KxesHwLLqaiSCXbHmPCdhk96C8ypFxrKhkt4hrjw6974GsZDXoG9 $ bx wif-to-ec L2Xh8eriuNoy4XLfRy33hPzdhWsBKdr1qardezCSG4jf2u2C44fp #WIF形式の秘密鍵を16進表記に戻す。 9e7002550203722cd1ca405bc4ffb2a3a65f33b02e097746208dbde99996b7f5 $ bx base58check-decode L2Xh8eriuNoy4XLfRy33hPzdhWsBKdr1qardezCSG4jf2 #BASE58Checkフォーマットのデコード wrapper { checksum 2140274518 payload 9e7002550203722cd1ca405bc4ffb2a3a65f33b02e097746208dbde99996b7f501 version 128 } $ bx base58check-encode 9e7002550203722cd1ca405bc4ffb2a3a65f33b02e097746208dbde99996b7f501 --version 128 #BASE58Checkフォーマットへのエンコード L2Xh8eriuNoy4XLfRy33hPzdhWsBKdr1qardezCSG4jf2u2C44fp
ビットコインアドレス
ビットコインのアドレスってどうやって生成されるのか疑問だったんですが、 Mastering bitcoinの続きにちゃんと載っていました。
ビットコインアドレス
ビットコインアドレスは、簡単に言うと公開鍵のハッシュ値です。
ウォレットの公開鍵
↓
SHA-256ハッシュ
↓
RIPMED160ハッシュ
↓
公開鍵ハッシュ(20バイト/160ビット)
だそうです。 ちなみに
SHA-256ハッシュ
↓
RIPMED160ハッシュ
の部分は、二重ハッシュ化またはHASH160と呼ぶそうです。
さらにBASE58Checkというエンコードを行います。 こまかいこというとその先頭には、version prefix 0x00というものをいれてるみたい。 その結果としてビットコインアドレスは、今まで使ってきたテキスト(アルファベットと数字)でみられるようになりますね。
なるほど
BASE58は、電子メールの添付ファイルで使われているBASE64のお仲間で、記号と見間違いやすいアルファベットOなど以外の数字とアルファベットだけにしたエンコード方式。
BASE58Checkは、BASE58にチェックサムをいれたもの。チェックサムは、元データにSHA256を2回かけたもので作られるらしい。
(追記) ビットコインアドレスは、後日書く代表的なウォレットであるHDウォレットとも関係が出てきます。