ビットコインブロックの構造

ビットコインのブロックの構造のまとめ

 ブロック構造

サイズ フィールド名 説明
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

いくつかの文面に同意した後

f:id:deconsawady:20190206052859p:plain

無事起動!! 有料だけど、30日間は無償らしい。

ちょこっと触ったけど、結構使える。

PyCharmのインストールと起動

PyCharmのホームページから無償のLINUX community版をダウンロード

$ mkdir ~/pycharm
$ mv pycham* ~/pycharm
$ cd pycharm-community-xxxx.x.x
$ cd bin
$ ./pycharm

f:id:deconsawady:20190206055137p:plain

こっちも無事起動!

ただ、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ウォレットとも関係が出てきます。