@mizumotokのブログ

テクノロジー、投資、書評、映画、筋トレなどについて

ブロックチェーンの中身を読んでみよう

ビットコインP2Pですべての人にオープンにされています。その取引を全部記したブロックチェーンは当然誰でも閲覧可能です。
Blockchain.infoというウェブサイトではブロックチェーンを読みやすい形式で公開してくれています。Blockchain.info上でをブロックチェーンの中身を見ることで、ブロックチェーンの仕組みについての理解を深めましょう。

f:id:mizumotok:20180411135101j:plain

ブロックチェーンはオープンな取引台帳

ビットコインP2P(Peer to Peer)ネットワーク上に構築されています。P2Pと対極にあるのがクライアント-サーバ方式で、クライアント-サーバ方式ではサーバが全データを管理するのが一般的です。一方、P2Pでは特定のサーバが存在しません。ビットコインではP2Pネットワークに接続されたコンピューター同士が通信しながら同じデータを保持しようとします。各端末で共有しているデータはビットコインの取引の履歴であり、ブロックチェーンそのものです。

ビットコイン送金ではP2Pネットワーク上に取引データ(トランザクション)を送ります。AさんからBさんに0.1BTC送る、といった取引の明細です。これをマイナーが約10分かかるマイニング処理をしてブロックをつくり、各端末にブロックを送信します。各端末ではブロックを受けったら内容をチェックして、その端末で保持しているブロックチェーンに追加していきます。この仕組はオープンソースとして全員に公開されており、ブロックチェーンも各端末で保持していますのでP2Pネットワーク参加者は誰でも閲覧できる状態にあります。

各端末に置かれているブロックチェーンはそのままでは読みやすいものとは言えませんが、Blockchainというウェブサイトで読みやすい形式にして広く公開されていますので、誰でも(P2Pネットワークに参加していない人でも)全トランザクションを閲覧することができます。

f:id:mizumotok:20180412032115j:plain

ブロックの中身

Heightはブロックチェーンにブロックが1つ追加されるごとに1つ増えます。つまりブロックチェーンに含まれるブロック数です。2018年には50万ブロックを超えています。運用開始以来50万回以上マイニングが行われブロックが作られたということです。

Transactionsはこのブロックに含まれるトランザクション数です。トランザクションには手数料が含まれておりこの手数料はマイニングしたマイナーがもらえるので、マイナーは多くの手数料を含むトランザクションをできるだけたくさんブロックに詰め込もうとします。しかしブロックにはサイズの上限が決められていますので、マイナーはこの上限を超えない範囲でできるだけ多くの手数料を得られるようにトランザクションを選んでブロックに含めていきます。

Total Sentは全トランザクションの送金額の合計です。金額の単位ではこのウェブサイトの右下(フッター部)で選択できます。BTCやドル、ユーロ、円やその他の通貨単位が用意されています。この記事内ではBTCに設定されているとして話を進めます。

Relayed Byはこのブロックをマイニングしたマイナーです。実際にはマイニングプールです。マイニングプールは多くのマイナー端末が共同でマイニングを行い、その中の誰かがマイニングに成功したらそのマイニングプール全体で報酬を分け合うという仕組みのことです。

ブロックの実例

実例としてHeightが2000のブロックの中身を見てみましょう。
https://blockchain.info/en/block/000000000000034a7dedef4a161fa058a2d67a173a90155f3a2fe6fc132e0ebf

Summary

Summaryには実際にブロックに含まれている値と計算等によって出された値があります。
Number Of TransactionsやOutput Totalはそれぞれトランザクション数と送金額合計を表していますが、これはブロック内のトランザクションから計算して求められます。
Summaryのうちで、実際にブロックに含まれるのはTimestamp、Bits、Version、Nonceくらいです。

class CBlockHeader
{
public:
    // header
    int32_t nVersion;
    uint256 hashPrevBlock;
    uint256 hashMerkleRoot;
    uint32_t nTime;
    uint32_t nBits;
    uint32_t nNonce;

GitHub

Timstampはブロックが生成された時刻、Nonceはマイニングで見つけられた値です。

Bitsは256ビットのDifficulty Target(ハッシュ値しきい値、マイニングでハッシュ値がこの値以下になるようにNonceを見つける)をある計算式で32ビットに変換した値です。

Versionはブロックの規格のバージョン(通常は2)を指しますが、ソフトフォーク(互換性を維持したままのアップデート)の際にそのアップデートの準備ができたかどうかをここでシグナルとして示すことができます。このシグナルがある一定数集まれば実際にソフトフォークが行われます。
例えばこのブロックでは0x12というシグナルがあります。0x12 = 10010 なのでbits4(BIP91)とbits1(BIP141)の2つのソフトフォークのシグナルを表しています。

Hashes

Hashはブロックのハッシュ値です。ハッシュ関数で簡単に求められますが、そのブロック自体には自身のハッシュ値を持たず、次のブロックに含まれます。
Previous Blockは直前のブロックのハッシュ値で、このブロックの持ちます。
Next Blocksは次のブロックのハッシュ値ですが、このブロックにはその値を保持していません。次のブロック(このブロックのハッシュ値をPrevious Blockに持つブロック)のハッシュ値を計算することで知ることができます。

Markel Rootは、このブロックに含まれる全トランザクションをマークルツリーと呼ぶ木構造で表したときのルートハッシュです。詳細は省きますが、マークルツリーを使えばあるトランザクションがこのブロックに含まれるかどうかを高速に検査することができます。トランザクションの検証時に、そのトランザクションが他のブロックで使われていなかどうか等で、ブロックチェーンの中からトランザクションを探すのに使います。またトランザクションデータは大きくなりがちですが、ブロックチェーンは持つがトランザクションデータを持たないようなウォレットのような軽量クライアントでもトランザクションの検証を行うことができます。

トランザクション

TransactionsはInputsとOutputsから成り立っています。

Outputsには送金額とScriptPubKeyが複数含まれます。ScriptPubKeyはこのアウトプットを使用する条件です。通常は送信先アドレスを入れることで、そのアドレスのみがこのアウトプットを使用できるようになります。

Inputsにはこのトランザクションハッシュ値、このトランザクションを生成するのに使用したトランザクション(UXTO)とScriptSigが複数含まれます。ScriptSigはUXTOをトランザクションとして使用する証拠を指定します。送信元の電子署名と公開鍵を入れておきます。電子署名はこの公開鍵で開けるのでこの公開鍵と対になる秘密鍵を持っていることを証明できます。また公開鍵からアドレスを作れますので、UXTOのアウトプットにのScriptPubKeyにあるアドレスはこの公開鍵の所有者、すなわち秘密鍵の所有者=送信者であることを証明することができるのです。

実際にはScriptPubKeyにもScriptSigにもスクリプト式を入れるのですが、上記の流れを行うようなスクリプトを入れています。

実際にトランザクションの詳細を見てます。

https://blockchain.info/en/tx/f7a0f2b9dfa73dc795ca42fc4104adaa0f8c8a268dbb9348c987ff037fcc73ae

Outputs(緑矢印の右側)にはアドレスと金額が入っています。アドレスは実際にはScriptPubKey(Output Scripts)から分かるのですが、ここでは分かりやすくアドレスで表示してくれています。

Inputs(緑矢印の左側)にはアドレスがありますが、本来はUXTOのハッシュ値が来ます。OutputのリンクをクリックすることでそのUXTOを参照することができます。

Inputs and Outputsの表にFee(手数料)という項目があります。手数料は明示的には指定せず、Inputs(UXTOのOutputs)に含まれる金額の合計とOutputsに含まれる合計の差が手数料となり、報酬に上乗せしてこのブロックをマイニングしたマイナーに支払われます。

報酬

ブロックのトランザクションにはInputsのないものが一つ含まれます。これはCoinBaseと呼ばれるトランザクションで、マイニングの報酬にあたるものです。決まった額の報酬(2018年4月現在は12.5BTC)に各トランザクションの手数料を上乗せしてマイナーのアドレスを指定して送金します。

https://blockchain.info/en/tx/dbaf14e1c476e76ea05a8b71921a46d6b06f0a950f17c5f9f1a03b8fae467f10

この当時(2012年9月22日)の報酬は50BTCだったようです。
CoinBaseにはInputsがない代わりに任意のデータを入れることができます。マイナーがメッセージを入れたりします。

アドレス

アドレスはブロックチェーン内の各ブロックのトランザクション内に入っていますが、Blockchainのウェブサイトでは逆にアドレスが使われているトランザクションのリストを表示することができます。

https://blockchain.info/en/address/1B5PQMZRuDXPBPFxW8WBh4h2dzGiLGsTWo

このアドレスから送金したり、受け取ったりした履歴が分かります。また最終的にこのアドレスの残高も分かります。

最初のブロック(Genesis block)

ブロックHeightが0である最初のブロックをGenesis blockと言います。

https://blockchain.info/block/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f

CoinBaseには以下のメッセージが入っています。

The Times 03/Jan/2009 Chancellor on brink of second bailout for banks

これは2009年1月3日のタイムス紙に掲載された実際の見出しです。このメッセージにはこの日より以前にはビットコインは存在しなかったという意味がこめられています。

Genesis blockはマイニングされたものでなく、プログラムにハードコードされています。

static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
{
    CMutableTransaction txNew;
    txNew.nVersion = 1;
    txNew.vin.resize(1);
    txNew.vout.resize(1);
    txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
    txNew.vout[0].nValue = genesisReward;
    txNew.vout[0].scriptPubKey = genesisOutputScript;

    CBlock genesis;
    genesis.nTime    = nTime;
    genesis.nBits    = nBits;
    genesis.nNonce   = nNonce;
    genesis.nVersion = nVersion;
    genesis.vtx.push_back(MakeTransactionRef(std::move(txNew)));
    genesis.hashPrevBlock.SetNull();
    genesis.hashMerkleRoot = BlockMerkleRoot(genesis);
    return genesis;
}

/**
 * Build the genesis block. Note that the output of its generation
 * transaction cannot be spent since it did not originally exist in the
 * database.
 *
 * CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1)
 *   CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0)
 *     CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73)
 *     CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
 *   vMerkleTree: 4a5e1e
 */
static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
{
    const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
    const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
    return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
}

GitHub

まとめ

  • Blockchain.infoのウェブサイトからブロックチェーンを全て閲覧できます
  • 実際にブロックに含まれるのはTimestamp、Bits、Version、Nonce、Previous Block、Markel Rootです
  • Markel Rootはトランザクションがこのブロックに含まれているかどうかを検証するのに使います
  • トランザクションにはInputsとOutputsからなり、Inputsを使えるという証明、Outputsを使用できる条件が含まれます
  • 報酬用のトランザクションはCoinBaseといい、Inputsを持ちません
  • ブロックチェーンの最初のブロックをGenesis blockと呼びます

ビットコインとブロックチェーン:暗号通貨を支える技術

ビットコインとブロックチェーン:暗号通貨を支える技術