RealPath:
WebPath:
2017/01/08 20:23 (JST) 更新
ブロック >>

ブロックの生成

Contents

ブロック生成時に未検証トランザクションがブロックに取り込まれる。

ブロックの生成には割と大きめのマシンパワーが必要になるが、regtest 環境においては difficulty が低く設定されているためマシンパワーをほぼ使わずにブロック生成を行える。regtest 環境におけるブロック生成は bitcoin-cli generate コマンドにより行う。

bitcoin-cli generate コマンドによるブロック生成の仕組みは mainnet や testnet におけるブロック生成と変わらないが、この generate コマンドでは極めて difficulty が低い環境でしかブロック生成が成功しない実装になっているため、mainnet や testnet において generate コマンドが成功する可能性はゼロではないが極めて低い。これらの環境においては一般的にマイニング専用の別ツールによりブロック生成が行われる。

このページでは regtest 環境を前提に説明を行う。

ブロック生成時に未検証トランザクションが取り込まれることの確認

まずトランザクションを適当に発生させる。

$ bitcoin-cli getnewaddress                                        … アドレス生成
mwFDTLxWPqsQUVUekB1ny9QFnVfzGsFdif                                 … 生成されたアドレス

$ bitcoin-cli sendtoaddress mwFDTLxWPqsQUVUekB1ny9QFnVfzGsFdif 0.1 … 生成されたアドレス宛てに送金
1e03ad7af098282b016da7d1b651fdf47d722b8eeb7b42144d17e9103a870e54   … トランザクションID

この時点で発生したトランザクション 1e03ad7af098282b016da7d1b651fdf47d722b8eeb7b42144d17e9103a870e54 はまだブロックに取り込まれていない(未検証トランザクション)。

 
未検証トランザクションの数は以下コマンドにより確認できる。

$ bitcoin-cli getmininginfo | grep pooledtx
  "pooledtx": 1,

 
続けてブロック生成を行う。

$ bitcoin-cli generate 1
[
  "2d0b915ed2b8b8124ce1d13b1378b42e1411d9fca6acb91dbea263ee5256b810"
]

ブロック 2d0b915ed2b8b8124ce1d13b1378b42e1411d9fca6acb91dbea263ee5256b810 が生成された。

このブロックにはさきほどの未検証トランザクションが取り込まれた形になっているはずである。それを以下コマンドにより確認する。

$ bitcoin-cli getblock 2d0b915ed2b8b8124ce1d13b1378b42e1411d9fca6acb91dbea263ee5256b810
{
  "hash": "2d0b915ed2b8b8124ce1d13b1378b42e1411d9fca6acb91dbea263ee5256b810",
  ....
  "tx": [
    "8091d87560894a93cf673d19f9d6baeaa8226d7f2cf28ce460827fa650bb9112", … coinbaseトランザクション
    "1e03ad7af098282b016da7d1b651fdf47d722b8eeb7b42144d17e9103a870e54"  … 取り込まれたトランザクション
  ],
  ....
}

さきほどのトランザクション 1e03ad7af098282b016da7d1b651fdf47d722b8eeb7b42144d17e9103a870e54 が取り込まれていることが tx リスト内で確認できる。

ブロック生成時には、そのときに存在していた未検証トランザクションが取り込まれるのに加えて、coinbase トランザクションというものも同時に生成され、tx の先頭に追加される。今回の場合であれば、トランザクション 8091d87560894a93cf673d19f9d6baeaa8226d7f2cf28ce460827fa650bb9112 が coinbase トランザクションにあたる。coinbase トランザクションはブロック生成者に対して採掘報酬(最初は 50BTC。半減期を迎えるごとにこの額は半減していく)を与える内容のトランザクションである。

coinbase トランザクション 8091d87560894a93cf673d19f9d6baeaa8226d7f2cf28ce460827fa650bb9112 の内容を見てみる。

$ bitcoin-cli gettransaction 8091d87560894a93cf673d19f9d6baeaa8226d7f2cf28ce460827fa650bb9112
{
  ....
  "details": [
    {
      "account": "",
      "address": "mrqZVQkqnaZ8kTjKjQRTxu4f9P7C4uVtr6", … 採掘者のアドレス
      "category": "immature", … この時点ではまだ報酬は未確定
      "amount": 50.00010420,  … 報酬額
      "vout": 0
    }
  ],
  ....
}

約 50 BTC の報酬を与えるトランザクションになっていることがわかる(微妙に端数があるのは、採掘報酬に加えてトランザクション送金手数料も少し上乗せされているからである)。ただし上記のように category が immature の状態ではまだトランザクションアウトプットは有効とはみなされない(この 50 BTC を誰かに送金することはできない)。今回のブロックの後ろに 100 ブロック (const int COINBASE_MATURITY = 100) 以上が追加されたとき、このトランザクションの category は generate となり、報酬 BTC が有効になる。

実際、以下のように 100 ブロックを追加で生成すると該当トランザクションの category は immature から generate に変化し、採掘報酬が確定する。

$ bitcoin-cli generate 100 … 100ブロック追加
$ bitcoin-cli gettransaction 8091d87560894a93cf673d19f9d6baeaa8226d7f2cf28ce460827fa650bb9112
{
  ....
  "details": [
    {
      "account": "",
      "address": "mxi9A22o2N2bFTfymu18uBhe8nuPJWgNVs",
      "category": "generate", … 報酬が確定した
      "amount": 50.00004520,
      "vout": 0
    }
  ],
  ....
}

ソースコード: coinbase トランザクションの category 判定部

rpcwallet.cpp
if (wtx.IsCoinBase())
{
    if (wtx.GetDepthInMainChain() < 1)
        entry.push_back(Pair("category", "orphan"));
    else if (wtx.GetBlocksToMaturity() > 0)
        entry.push_back(Pair("category", "immature"));
    else
        entry.push_back(Pair("category", "generate"));
}

genesis ブロックに含まれるトランザクション

$ bitcoin-cli getblockhash 0
0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206

$ bitcoin-cli getblock 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206
{
  ....
  "tx": [
    "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
  ],
  ....
}

※ここまでは取得できるがこのトランザクションはハードコーディングされているため、bitcoin-cli により詳細を取得することはできない。

ソースコード: generate コマンドによるブロック生成

UniValue generate(const JSONRPCRequest& request)
{
   ....
   boost::shared_ptr<CReserveScript> coinbaseScript;
   GetMainSignals().ScriptForMining(coinbaseScript);
   ....
   return generateBlocks(coinbaseScript, nGenerate, nMaxTries, true);
}
↓
// nGenerate 個のブロックを作る関数。
// ブロックハッシュのリストを返す。(ブロック生成に失敗した場合は空リストを返す)
UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
{
    static const int nInnerLoopCount = 0x10000;
    ....
    UniValue blockHashes(UniValue::VARR);
    while (nHeight < nHeightEnd)
    {
        BlockAssembler blockAssembler(Params());

        // ブロックテンプレート生成(nonce が未確定なのでまだ正式なブロックではない)
        std::unique_ptr<CBlockTemplate> pblocktemplate(blockAssembler.CreateNewBlock(coinbaseScript->reserveScript));
        if (!pblocktemplate.get())
            throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");

        // ここでブロックの nonce 値を総当たりで求める(regtest 環境においては大抵10回以内には nonce が求まる)
        CBlock *pblock = &pblocktemplate->block;
        {
            LOCK(cs_main);
            IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
        }
        while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
            ++pblock->nNonce;
            --nMaxTries;
        }
        if (nMaxTries == 0) {
            break;
        }
        if (pblock->nNonce == nInnerLoopCount) {
            continue;
        }

        // ここまで来たら nonce 値が求まっている状態
        std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
        if (!ProcessNewBlock(Params(), shared_pblock, true, NULL))
            throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
        ++nHeight;
        blockHashes.push_back(pblock->GetHash().GetHex());

        //mark script as important because it was used at least for one coinbase output if the script came from the wallet
        if (keepScript)
        {
            coinbaseScript->KeepScript();
        }
    }
    return blockHashes;
}

 
ブロックにトランザクション詰め込んでるところ

std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
{
    ....

    // vtx[0] (先頭トランザクション) を coinbase トランザクションとするために、一旦空きを作っておく
    // Add dummy coinbase tx as first transaction
    pblock->vtx.emplace_back();
    pblocktemplate->vTxFees.push_back(-1); // updated at end
    pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end

    // 未検証トランザクションリストに割り込みが入らないようにロックしておく
    LOCK2(cs_main, mempool.cs);

    // ブロックバージョンの算出
    CBlockIndex* pindexPrev = chainActive.Tip();
    nHeight = pindexPrev->nHeight + 1;

    pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
    // -regtest only: allow overriding block.nVersion with
    // -blockversion=N to test forking scenarios
    if (chainparams.MineBlocksOnDemand())
        pblock->nVersion = GetArg("-blockversion", pblock->nVersion);

    pblock->nTime = GetAdjustedTime();
    const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();

    nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
                       ? nMedianTimePast
                       : pblock->GetBlockTime();

    // Decide whether to include witness transactions
    // This is only needed in case the witness softfork activation is reverted
    // (which would require a very deep reorganization) or when
    // -promiscuousmempoolflags is used.
    // TODO: replace this with a call to main to assess validity of a mempool
    // transaction (which in most cases can be a no-op).
    fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus());

    addPriorityTxs();
    addPackageTxs();

    nLastBlockTx = nBlockTx;
    nLastBlockSize = nBlockSize;
    nLastBlockWeight = nBlockWeight;

    // coinbase トランザクションの埋め込み
    // Create coinbase transaction.
    CMutableTransaction coinbaseTx;
    coinbaseTx.vin.resize(1);
    coinbaseTx.vin[0].prevout.SetNull();
    coinbaseTx.vout.resize(1);
    coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
    coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); // 採掘報酬額
    coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
    pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
    pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus());
    pblocktemplate->vTxFees[0] = -nFees;

    uint64_t nSerializeSize = GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION);
    LogPrintf("CreateNewBlock(): total size: %u block weight: %u txs: %u fees: %ld sigops %d\n", nSerializeSize, GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);

    // Fill in header
    pblock->hashPrevBlock  = pindexPrev->GetBlockHash();
    UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
    pblock->nBits          = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
    pblock->nNonce         = 0;
    pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);

    CValidationState state;
    if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
        throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state)));
    }

    return std::move(pblocktemplate);
}