比特幣源碼分析——共識模塊


title: 比特幣源碼分析——共識模塊
date: 2021-11-02 18:39:52


前言

分析比特幣系統(tǒng)的共識模塊,包括共識算法、交易的流程等,基于比特幣開源客戶端Bitcoin Core v22.0 版本的源碼。

比特幣中共識算法的大體步驟如下:

  1. 新交易被創(chuàng)建并廣播到比特幣網(wǎng)絡中。

  2. 每個節(jié)點接收到交易后,獨立地對交易進行驗證。

  3. 礦工節(jié)點將新交易收集到一個區(qū)塊中,并為該區(qū)塊尋找工作量證明,然后將新區(qū)塊廣播到網(wǎng)絡中。

  4. 每個節(jié)點收到區(qū)塊后,對區(qū)塊進行獨立的校驗,并組裝進區(qū)塊鏈中。

  5. 每個節(jié)點對區(qū)塊鏈進行獨立選擇,選擇最大工作量證明的鏈。

接下來,將結(jié)合源碼講解分析上述步驟。

創(chuàng)建交易

在Bitcoin Core客戶端中,用戶可以通過 $ bitcoin-cli createrawtransaction命令創(chuàng)建一筆交易。這個命令背后對應著一個RPC接口,并映射到一個處理函數(shù)。映射關(guān)系定義在src\rpc\rawtransaction.cpp中:

// 注冊交易的RPC接口及其對應的處理函數(shù)
void RegisterRawTransactionRPCCommands(CRPCTable &t)
{
// clang-format off
static const CRPCCommand commands[] =
{ //  category               actor (function)
  //  ---------------------  -----------------------
    { "rawtransactions",     &getrawtransaction,          },
    { "rawtransactions",     &createrawtransaction,       },
    { "rawtransactions",     &decoderawtransaction,       },
    { "rawtransactions",     &decodescript,               },
    { "rawtransactions",     &sendrawtransaction,         },
    { "rawtransactions",     &combinerawtransaction,      },
    { "rawtransactions",     &signrawtransactionwithkey,  },
    { "rawtransactions",     &testmempoolaccept,          },
    { "rawtransactions",     &decodepsbt,                 },
    { "rawtransactions",     &combinepsbt,                },
    { "rawtransactions",     &finalizepsbt,               },
    { "rawtransactions",     &createpsbt,                 },
    { "rawtransactions",     &converttopsbt,              },
    { "rawtransactions",     &utxoupdatepsbt,             },
    { "rawtransactions",     &joinpsbts,                  },
    { "rawtransactions",     &analyzepsbt,                },

    { "blockchain",          &gettxoutproof,              },
    { "blockchain",          &verifytxoutproof,           },
};
// clang-format on
    for (const auto& c : commands) {
        t.appendCommand(c.name, &c);
    }
}

該注冊函數(shù)的被調(diào)用關(guān)系圖如下:

1.png

創(chuàng)建交易RPC接口對應的處理函數(shù)如下:

static RPCHelpMan createrawtransaction()
{
    return RPCHelpMan{"createrawtransaction",
                "\nCreate a transaction spending the given inputs and creating new outputs.\n"
                "Outputs can be addresses or data.\n"
                "Returns hex-encoded raw transaction.\n"
                "Note that the transaction's inputs are not signed, and\n"
                "it is not stored in the wallet or transmitted to the network.\n",
                {
                    {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs",
                        {
                            {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
                                {
                                    {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
                                    {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
                                    {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'replaceable' and 'locktime' arguments"}, "The sequence number"},
                                },
                            },
                        },
                    },
                    {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n"
                            "That is, each address can only appear once and there can only be one 'data' object.\n"
                            "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
                            "                             accepted as second parameter.",
                        {
                            {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
                                {
                                    {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT},
                                },
                                },
                            {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
                                {
                                    {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
                                },
                                },
                        },
                        },
                    {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
                    {"replaceable", RPCArg::Type::BOOL, RPCArg::Default{false}, "Marks this transaction as BIP125-replaceable.\n"
            "                             Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."},
                },
                RPCResult{
                    RPCResult::Type::STR_HEX, "transaction", "hex string of the transaction"
                },
                RPCExamples{
                    HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"")
            + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
            + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"")
            + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
                },
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
    // 檢查四個參數(shù) inputs outputs locktime replaceable 類型是否正確
    RPCTypeCheck(request.params, {
        UniValue::VARR,
        UniValueType(), // ARR or OBJ, checked later
        UniValue::VNUM,
        UniValue::VBOOL
        }, true
    );

    bool rbf = false;
    if (!request.params[3].isNull()) {
        rbf = request.params[3].isTrue();
    }
    // 構(gòu)造交易(包括交易的輸入vin、輸出vout及鎖定腳本),返回序列化數(shù)據(jù)
    CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);

    // 哈希
    return EncodeHexTx(CTransaction(rawTx));
},
    };
}

函數(shù)定義了命令的說明、請求參數(shù)、返回值與內(nèi)部實現(xiàn)過程,主要是根據(jù)請求參數(shù)構(gòu)造一筆交易,然后返回交易的哈希值。構(gòu)造的過程主要包括構(gòu)造交易的輸入vin、輸出vout及鎖定腳本 ,具體內(nèi)容不在此講解。

構(gòu)建交易之后,需要使用$ bitcoin-cli signrawtransactionwithkey對交易進行簽名,其處理函數(shù)也在src\rpc\rawtransaction.cpp中,主要內(nèi)容是構(gòu)建vin中的解鎖腳本 。

簽名后,就可以使用$ bitcoin-cli sendrawtransaction將交易廣播至比特幣網(wǎng)絡中,具體實現(xiàn)代碼不在此展示。

驗證交易

Bitcoin Core客戶端可通過bitcoind命令啟動,啟動的入口函數(shù)是src\bitcoind.cpp中的main函數(shù)。啟動之后,Bitcoin Core會啟動一個線程用于監(jiān)聽、接收并響應比特幣網(wǎng)絡中的信息,該線程對應的函數(shù)CConnman::ThreadMessageHandlersrc\net.cpp中。

該函數(shù)會調(diào)用src\net_processing.cpp中的PeerManagerImpl::ProcessMessages函數(shù)處理接收到的信息,而它會進一步調(diào)用同在src\net_processing.cpp中的PeerManagerImpl::ProcessMessage函數(shù)。其被調(diào)用的關(guān)系如下:

main -> AppInit -> AppInitMain -> CConnman::Start -> CConnman::ThreadMessageHandler
-> PeerManagerImpl::ProcessMessages -> PeerManagerImpl::ProcessMessage

PeerManagerImpl::ProcessMessage函數(shù)中,會根據(jù)信息類型進行不同的處理,如果是交易類型的消息,則會調(diào)用src\validation.cpp中的AcceptToMemoryPool函數(shù)將交易存放到交易池中。

void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
                                     const std::chrono::microseconds time_received,
                                     const std::atomic<bool>& interruptMsgProc)
{
    ......

    // 如果消息是交易類型
    if (msg_type == NetMsgType::TX) {
        ......

        // 接收到交易池中
        const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, ptx, false /* bypass_limits */);

        ......
    }

    ......
}

AcceptToMemoryPool函數(shù)進而調(diào)用AcceptToMemoryPoolWithTime函數(shù),接著調(diào)用MemPoolAccept:: AcceptSingleTransaction函數(shù)。

而這個MemPoolAccept::AcceptSingleTransaction函數(shù),是接收交易的入口函數(shù)。在這個函數(shù)中,又會調(diào)用MemPoolAccept::PreChecks函數(shù),它是關(guān)鍵中的關(guān)鍵,負責對交易進行全方位的檢查。在這個函數(shù)中:

  1. 首先,會調(diào)用src\consensus\tx_check.cpp中的CheckTransaction函數(shù),進行基礎(chǔ)的檢查:
bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
{
    // 1. 檢查交易的輸入輸出是否為空
    // Basic checks that don't depend on any context
    if (tx.vin.empty())
        return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vin-empty");
    if (tx.vout.empty())
        return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-empty");

    // 2. 檢查交易大小不能超過MAX_BLOCK_WEIGHT
    // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
    if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
        return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-oversize");

    // 3. 檢查輸出值不能為負數(shù)或者超過范圍
    // Check for negative or overflow output values (see CVE-2010-5139)
    CAmount nValueOut = 0;
    for (const auto& txout : tx.vout) {
        if (txout.nValue < 0)
            return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-negative");
        if (txout.nValue > MAX_MONEY)
            return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-toolarge");
        nValueOut += txout.nValue;
        if (!MoneyRange(nValueOut))
            return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-txouttotal-toolarge");
    }

    // 4. 檢查是否有重復的輸入
    // Check for duplicate inputs (see CVE-2018-17144)
    // While Consensus::CheckTxInputs does check if all inputs of a tx are available, and UpdateCoins marks all inputs
    // of a tx as spent, it does not check if the tx has duplicate inputs.
    // Failure to run this check will result in either a crash or an inflation bug, depending on the implementation of
    // the underlying coins database.
    std::set<COutPoint> vInOutPoints;
    for (const auto& txin : tx.vin) {
        if (!vInOutPoints.insert(txin.prevout).second)
            return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate");
    }

    if (tx.IsCoinBase()) {
        // 5.1. 如果是鑄幣交易,檢查輸入中解鎖腳本的大小
        if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
            return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cb-length");
    }
    else {
        // 5.2. 若不是鑄幣交易,檢查輸入對應的來源不能為空
        for (const auto& txin : tx.vin)
            if (txin.prevout.IsNull())
                return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-prevout-null");
    }

    return true;
}
  1. 這筆被接受的交易不能是鑄幣交易:
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
    return state.Invalid(TxValidationResult::TX_CONSENSUS, "coinbase");
  1. 檢查是否為標準交易,調(diào)用src\policy\policy.cpp中的IsStandardTx函數(shù),檢查交易的版本、大小、腳本、輸出中UTXO個數(shù)等是否符合標準。
std::string reason;
if (fRequireStandard && !IsStandardTx(tx, reason))
    return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason);
  1. 交易的字節(jié)大小不能太小。
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE)
    return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "tx-size-small");
  1. 只接受nLockTime滿足要求、能夠被打包進下一個被挖區(qū)塊中的交易,防止交易過多溢出交易池。
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
if (!CheckFinalTx(m_active_chainstate.m_chain.Tip(), tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
    return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
  1. 檢查這筆交易是否已存在交易池中,或有相同未認證的數(shù)據(jù)已在交易池中。
  if (m_pool.exists(GenTxid(true, tx.GetWitnessHash()))) {
      // Exact transaction already exists in the mempool.
      return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-already-in-mempool");
  } else if (m_pool.exists(GenTxid(false, tx.GetHash()))) {
      // Transaction with the same non-witness data but different witness (same txid, different
      // wtxid) already exists in the mempool.
      return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-same-nonwitness-data-in-mempool");
  }
  1. 檢查交易輸入所指向的上一筆交易輸出prevout是否與交易池中某筆交易的一樣,即防止雙花。
// Check for conflicts with in-memory transactions
for (const CTxIn &txin : tx.vin)
{
    const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
    if (ptxConflicting) {
        ......
    }
}
  1. 檢查交易所有輸入的來源(UTXO)是否已在緩存中,若不在則獲取。
const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip();
// do all inputs exist?
for (const CTxIn& txin : tx.vin) {
    if (!coins_cache.HaveCoinInCache(txin.prevout)) {
        coins_to_uncache.push_back(txin.prevout);
    }

    // Note: this call may add txin.prevout to the coins cache
    // (coins_cache.cacheCoins) by way of FetchCoin(). It should be removed
    // later (via coins_to_uncache) if this tx turns out to be invalid.
    if (!m_view.HaveCoin(txin.prevout)) {
       ......
    }
}
  1. 檢查時間鎖sequence是否滿足要求,即可以被打包進下一個待挖區(qū)塊中,不然就丟棄。
// Only accept BIP68 sequence locked transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
// Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
// backend was removed, it no longer pulls coins from the mempool.
if (!CheckSequenceLocks(m_active_chainstate.m_chain.Tip(), m_view, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
    return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
  1. 調(diào)用src\consensus\tx_verify.cpp中的Consensus::CheckTxInputs函數(shù)檢查交易的輸入。
bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
{
    // 1. 檢查輸入的來源(UTXO)是否都在緩存中
    // are the actual inputs available?
    if (!inputs.HaveInputs(tx)) {
        return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, "bad-txns-inputs-missingorspent",
                         strprintf("%s: inputs missing/spent", __func__));
    }

    CAmount nValueIn = 0;
    for (unsigned int i = 0; i < tx.vin.size(); ++i) {
        const COutPoint &prevout = tx.vin[i].prevout;
        const Coin& coin = inputs.AccessCoin(prevout);
        // 2. 輸入來源不能被雙花
        assert(!coin.IsSpent());

        // 3. 如果來源是coinbase,檢查是否成熟(確認數(shù)不小于100)
        // If prev is coinbase, check that it's matured
        if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
            return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase",
                strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
        }

        // 4. 檢查每一個輸入值和總值是否在限定范圍內(nèi)
        // Check for negative or overflow input values
        nValueIn += coin.out.nValue;
        if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
            return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputvalues-outofrange");
        }
    }

    // 5. 交易輸出總值不能大于輸入總值
    const CAmount value_out = tx.GetValueOut();
    if (nValueIn < value_out) {
        return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-in-belowout",
            strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
    }

    // 6. 交易的礦工費用需在規(guī)定范圍內(nèi)
    // Tally transaction fees
    const CAmount txfee_aux = nValueIn - value_out;
    if (!MoneyRange(txfee_aux)) {
        return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange");
    }

    txfee = txfee_aux;
    return true;
}
  1. 檢查輸入和輸入的見證腳本是否符合標準(解鎖腳本能否解開prevout的鎖定腳本)。
// Check for non-standard pay-to-script-hash in inputs
const bool taproot_active = DeploymentActiveAfter(m_active_chainstate.m_chain.Tip(), args.m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_TAPROOT);
if (fRequireStandard && !AreInputsStandard(tx, m_view, taproot_active)) {
    return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
}

// Check for non-standard witnesses.
if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, m_view))
    return state.Invalid(TxValidationResult::TX_WITNESS_MUTATED, "bad-witness-nonstandard");
  1. 交易中的簽名數(shù)量(sigops)應小于簽名操作數(shù)量上限。
if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
    return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
            strprintf("%d", nSigOpsCost));

創(chuàng)建區(qū)塊

在Bitcoin Core中,可通過$ bitcoin-cli generatetoaddress命令進行挖礦,命令內(nèi)部實現(xiàn)定義在src\rpc\mining.cppgeneratetoaddress函數(shù)中:

static RPCHelpMan generatetoaddress()
{
    return RPCHelpMan{"generatetoaddress",
                "\nMine blocks immediately to a specified address (before the RPC call returns)\n",
                {
                    {"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
                    {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address to send the newly generated bitcoin to."},
                    {"maxtries", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_MAX_TRIES}, "How many iterations to try."},
                },
                RPCResult{
                    RPCResult::Type::ARR, "", "hashes of blocks generated",
                    {
                        {RPCResult::Type::STR_HEX, "", "blockhash"},
                    }},
                RPCExamples{
            "\nGenerate 11 blocks to myaddress\n"
            + HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
            + "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated bitcoin to with:\n"
            + HelpExampleCli("getnewaddress", "")
                },
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
    // 要創(chuàng)建區(qū)塊的個數(shù)
    const int num_blocks{request.params[0].get_int()};
    // 嘗試次數(shù)
    const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};

    // 創(chuàng)建區(qū)塊所獲得的coinbase獎勵轉(zhuǎn)入的地址
    CTxDestination destination = DecodeDestination(request.params[1].get_str());
    if (!IsValidDestination(destination)) {
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
    }

    NodeContext& node = EnsureAnyNodeContext(request.context);
    const CTxMemPool& mempool = EnsureMemPool(node);
    ChainstateManager& chainman = EnsureChainman(node);

    CScript coinbase_script = GetScriptForDestination(destination);

    // 開始創(chuàng)建區(qū)塊
    return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
},
    };
}

進行一系列檢查后,它將調(diào)用同在src\rpc\mining.cpp中的generateBlocks函數(shù),它是創(chuàng)建區(qū)塊的入口函數(shù):

static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
{
    int nHeightEnd = 0;
    int nHeight = 0;

    {   // Don't keep cs_main locked
        LOCK(cs_main);
        // 區(qū)塊鏈當前高度
        nHeight = chainman.ActiveChain().Height();
        // 創(chuàng)建nGenerate個區(qū)塊后的高度
        nHeightEnd = nHeight+nGenerate;
    }
    unsigned int nExtraNonce = 0;
    UniValue blockHashes(UniValue::VARR);
    // 開始構(gòu)造區(qū)塊并挖礦
    while (nHeight < nHeightEnd && !ShutdownRequested())
    {
        // 調(diào)用 BlockAssembler::CreateNewBlock 構(gòu)造候選區(qū)塊
        // chainman.ActiveChainstate()會獲取當前最長鏈,新區(qū)塊將會基于最長鏈延續(xù)
        std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(chainman.ActiveChainstate(), mempool, Params()).CreateNewBlock(coinbase_script));
        if (!pblocktemplate.get())
            throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
        CBlock *pblock = &pblocktemplate->block;

        // 調(diào)用GenerateBlock進行挖礦:遍歷區(qū)塊的nonce值,使得區(qū)塊哈希值滿足工作量證明
        uint256 block_hash;
        if (!GenerateBlock(chainman, *pblock, nMaxTries, nExtraNonce, block_hash)) {
            break;
        }

        if (!block_hash.IsNull()) {
            ++nHeight;
            blockHashes.push_back(block_hash.GetHex());
        }
    }
    return blockHashes;
}

該函數(shù)調(diào)用了兩個關(guān)鍵函數(shù):

CreateNewBlock

src\miner.cpp中的BlockAssembler::CreateNewBlock函數(shù),用于構(gòu)造候選區(qū)塊:

std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
{
    int64_t nTimeStart = GetTimeMicros();

    resetBlock();

    pblocktemplate.reset(new CBlockTemplate());

    if(!pblocktemplate.get())
        return nullptr;
    CBlock* const pblock = &pblocktemplate->block; // pointer for convenience

    // Add dummy coinbase tx as first transaction
    // 1. 區(qū)塊中的第一個位置留給coinbase交易
    pblock->vtx.emplace_back();
    pblocktemplate->vTxFees.push_back(-1); // updated at end
    pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end

    LOCK2(cs_main, m_mempool.cs);
    // 2. 取當前區(qū)塊鏈的最后一個節(jié)點,作為新區(qū)塊的父區(qū)塊
    CBlockIndex* pindexPrev = m_chainstate.m_chain.Tip();
    assert(pindexPrev != nullptr);
    nHeight = pindexPrev->nHeight + 1;

    // 3. 計算區(qū)塊版本
    pblock->nVersion = g_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
    // -regtest only: allow overriding block.nVersion with
    // -blockversion=N to test forking scenarios
    if (chainparams.MineBlocksOnDemand())
        pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion);

    // 4. 計算時間戳
    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).
    // Note that the mempool would accept transactions with witness data before
    // the deployment is active, but we would only ever mine blocks after activation
    // unless there is a massive block reorganization with the witness softfork
    // not activated.
    // 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 = DeploymentActiveAfter(pindexPrev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT);

    // 5. 從交易池中選擇一批交易打包到區(qū)塊中(注意:并不會從交易持中將交易刪除,刪除需要等區(qū)塊確認以后)
    int nPackagesSelected = 0;
    int nDescendantsUpdated = 0;
    addPackageTxs(nPackagesSelected, nDescendantsUpdated);

    int64_t nTime1 = GetTimeMicros();

    m_last_block_num_txs = nBlockTx;
    m_last_block_weight = nBlockWeight;

    // 6. 生成鑄幣交易
    // Create coinbase transaction.
    CMutableTransaction coinbaseTx;
    coinbaseTx.vin.resize(1);
    coinbaseTx.vin[0].prevout.SetNull();
    coinbaseTx.vout.resize(1);
    coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
    // 礦工獎勵=區(qū)塊中交易費用總和(在addPackageTxs時會進行統(tǒng)計)+系統(tǒng)發(fā)放獎勵
    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;

    LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);

    // 7. 填充區(qū)塊頭
    // Fill in header
    pblock->hashPrevBlock  = pindexPrev->GetBlockHash();
    UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
    // 設置新區(qū)塊的工作量難度目標值
    pblock->nBits          = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
    pblock->nNonce         = 0;
    pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);

    BlockValidationState state;
    if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) {
        throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
    }
    int64_t nTime2 = GetTimeMicros();

    LogPrint(BCLog::BENCH, "CreateNewBlock() packages: %.2fms (%d packages, %d updated descendants), validity: %.2fms (total %.2fms)\n", 0.001 * (nTime1 - nTimeStart), nPackagesSelected, nDescendantsUpdated, 0.001 * (nTime2 - nTime1), 0.001 * (nTime2 - nTimeStart));

    return std::move(pblocktemplate);
}

GenerateBlock

src\rpc\mining.cpp中的GenerateBlock函數(shù),用于挖礦:

static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t& max_tries, unsigned int& extra_nonce, uint256& block_hash)
{
    block_hash.SetNull();

    {
        LOCK(cs_main);
        // 在這個函數(shù)中會調(diào)用src\consensus\merkle.cpp中的BlockMerkleRoot函數(shù)
        // 計算區(qū)塊MerkleRoot的值
        IncrementExtraNonce(&block, chainman.ActiveChain().Tip(), extra_nonce);
    }

    CChainParams chainparams(Params());

    // 遍歷nonce值尋找工作量證明。
    // 調(diào)用src\pow.cpp中的CheckProofOfWork函數(shù)檢查是否滿足工作量證明,即區(qū)塊哈希小于目標值
    while (max_tries > 0 && block.nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus()) && !ShutdownRequested()) {
        ++block.nNonce;
        --max_tries;
    }
    if (max_tries == 0 || ShutdownRequested()) {
        return false;
    }
    if (block.nNonce == std::numeric_limits<uint32_t>::max()) {
        return true;
    }

    // 調(diào)用src\validation.cpp中的ChainstateManager::ProcessNewBlock函數(shù)
    // 處理新區(qū)塊:驗證區(qū)塊、接收區(qū)塊(鏈接到對應鏈上、廣播到網(wǎng)絡中等)、更新最長鏈
    std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
    if (!chainman.ProcessNewBlock(chainparams, shared_pblock, true, nullptr)) {
        throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
    }

    block_hash = block.GetHash();
    return true;
}

處理區(qū)塊

書接上文,創(chuàng)建區(qū)塊之后,會調(diào)用src\validation.cpp中的ChainstateManager::ProcessNewBlock函數(shù)處理新區(qū)塊。

同樣,在接收到其它節(jié)點傳播來的區(qū)塊信息后,也會調(diào)用這個函數(shù)?!膀炞C交易”一節(jié)中提到src\net_processing.cpp中的PeerManagerImpl::ProcessMessage函數(shù)用于處理從網(wǎng)絡中接收到的信息,如果信息是區(qū)塊類型,則會調(diào)用PeerManagerImpl::ProcessBlock函數(shù)。

void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
                                     const std::chrono::microseconds time_received,
                                     const std::atomic<bool>& interruptMsgProc)
{
    ......
    if (msg_type == NetMsgType::BLOCK)
    {
        ......
        ProcessBlock(pfrom, pblock, forceProcessing);
        return;
    }
    ......
}

PeerManagerImpl::ProcessBlock函數(shù)則是直接調(diào)用ChainstateManager::ProcessNewBlock函數(shù)。

void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing)
{
    bool new_block{false};
    m_chainman.ProcessNewBlock(m_chainparams, block, force_processing, &new_block);
    if (new_block) {
        node.nLastBlockTime = GetTime();
    } else {
        LOCK(cs_main);
        mapBlockSource.erase(block->GetHash());
    }
}

src\validation.cpp中的ChainstateManager::ProcessNewBlock函數(shù)是處理新區(qū)塊的入口函數(shù)/關(guān)鍵函數(shù),它將主要負責:驗證區(qū)塊、接收區(qū)塊、更新最長鏈。

bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
{
    AssertLockNotHeld(cs_main);

    {
        CBlockIndex *pindex = nullptr;
        if (new_block) *new_block = false;
        BlockValidationState state;

        // CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race.
        // Therefore, the following critical section must include the CheckBlock() call as well.
        LOCK(cs_main);

        // 1. 驗證區(qū)塊
        // Skipping AcceptBlock() for CheckBlock() failures means that we will never mark a block as invalid if
        // CheckBlock() fails.  This is protective against consensus failure if there are any unknown forms of block
        // malleability that cause CheckBlock() to fail; see e.g. CVE-2012-2459 and
        // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016697.html.  Because CheckBlock() is
        // not very expensive, the anti-DoS benefits of caching failure (of a definitely-invalid block) are not substantial.
        bool ret = CheckBlock(*block, state, chainparams.GetConsensus());
        if (ret) {
            // 2. 接收區(qū)塊
            // Store to disk
            ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block);
        }
        if (!ret) {
            GetMainSignals().BlockChecked(*block, state);
            return error("%s: AcceptBlock FAILED (%s)", __func__, state.ToString());
        }
    }

    NotifyHeaderTip(ActiveChainstate());

    BlockValidationState state; // Only used to report errors, not invalidity - ignore it
    // 3. 更新當前鏈為最長鏈
    if (!ActiveChainstate().ActivateBestChain(state, block)) {
        return error("%s: ActivateBestChain failed (%s)", __func__, state.ToString());
    }

    return true;
}

它主要調(diào)用如下三個函數(shù)。

CheckBlock

src\validation.cpp中的CheckBlock函數(shù),負責對區(qū)塊進行檢查:

// fCheckPOW和fCheckMerkleRoot兩個參數(shù)的默認值是true
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot)
{
    // These are checks that are independent of context.

    // 如果區(qū)塊已驗證,則直接返回
    if (block.fChecked)
        return true;

    // 1. 檢查區(qū)塊頭,是否滿足工作量證明
    // Check that the header is valid (particularly PoW).  This is mostly
    // redundant with the call in AcceptBlockHeader.
    if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW))
        return false;

    // Signet only: check block solution
    if (consensusParams.signet_blocks && fCheckPOW && !CheckSignetBlockSolution(block, consensusParams)) {
        return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-signet-blksig", "signet block signature validation failure");
    }

    // 2. 檢查merkle root值是否正確
    // Check the merkle root.
    if (fCheckMerkleRoot) {
        bool mutated;
        uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
        if (block.hashMerkleRoot != hashMerkleRoot2)
            return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txnmrklroot", "hashMerkleRoot mismatch");

        // Check for merkle tree malleability (CVE-2012-2459): repeating sequences
        // of transactions in a block without affecting the merkle root of a block,
        // while still invalidating it.
        if (mutated)
            return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txns-duplicate", "duplicate transaction");
    }

    // All potential-corruption validation must be done before we do any
    // transaction validation, as otherwise we may mark the header as invalid
    // because we receive the wrong transactions for it.
    // Note that witness malleability is checked in ContextualCheckBlock, so no
    // checks that use witness data may be performed here.

    // 3. 檢查區(qū)塊大小是否在規(guī)定范圍內(nèi)
    // Size limits
    if (block.vtx.empty() || block.vtx.size() * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT || ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
        return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-length", "size limits failed");

    // 4. 第一筆交易必須是coinbase交易,剩余的都不能是coinbase交易
    // First transaction must be coinbase, the rest must not be
    if (block.vtx.empty() || !block.vtx[0]->IsCoinBase())
        return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-missing", "first tx is not coinbase");
    for (unsigned int i = 1; i < block.vtx.size(); i++)
        if (block.vtx[i]->IsCoinBase())
            return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-multiple", "more than one coinbase");

    // 5. 調(diào)用src\consensus\tx_check.cpp中的CheckTransaction函數(shù)(上文已介紹)檢查每筆交易是否正確,
    // Check transactions
    // Must check for duplicate inputs (see CVE-2018-17144)
    for (const auto& tx : block.vtx) {
        TxValidationState tx_state;
        if (!CheckTransaction(*tx, tx_state)) {
            // CheckBlock() does context-free validation checks. The only
            // possible failures are consensus failures.
            assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS);
            return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(),
                                 strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), tx_state.GetDebugMessage()));
        }
    }

    // 6. sigops個數(shù)不能超過限定值
    unsigned int nSigOps = 0;
    for (const auto& tx : block.vtx)
    {
        nSigOps += GetLegacySigOpCount(*tx);
    }
    if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST)
        return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops", "out-of-bounds SigOpCount");

    if (fCheckPOW && fCheckMerkleRoot)
        block.fChecked = true;

    return true;
}

AcceptBlock

src\validation.cpp中的CChainState::AcceptBlock函數(shù),用于接收區(qū)塊,負責基本的驗證、鏈接到對應鏈上、廣播到網(wǎng)絡中、保存到本地磁盤等。

/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock)
{
    const CBlock& block = *pblock;

    if (fNewBlock) *fNewBlock = false;
    AssertLockHeld(cs_main);

    CBlockIndex *pindexDummy = nullptr;
    CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy;

    // 1. 接收區(qū)塊頭:
    //   檢查是否有重復區(qū)塊頭、是否存在父區(qū)塊、是否延續(xù)于不合法區(qū)塊之后(分叉情況)
    //   將區(qū)塊頭鏈接到對應的鏈上(可能會出現(xiàn)分叉情況)
    bool accepted_header = m_blockman.AcceptBlockHeader(block, state, m_params, &pindex);
    CheckBlockIndex();

    if (!accepted_header)
        return false;

    // Try to process all requested blocks that we don't have, but only
    // process an unrequested block if it's new and has enough work to
    // advance our tip, and isn't too many blocks ahead.
    bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
    bool fHasMoreOrSameWork = (m_chain.Tip() ? pindex->nChainWork >= m_chain.Tip()->nChainWork : true);
    // Blocks that are too out-of-order needlessly limit the effectiveness of
    // pruning, because pruning will not delete block files that contain any
    // blocks which are too close in height to the tip.  Apply this test
    // regardless of whether pruning is enabled; it should generally be safe to
    // not process unrequested blocks.
    bool fTooFarAhead = (pindex->nHeight > int(m_chain.Height() + MIN_BLOCKS_TO_KEEP));

    // TODO: Decouple this function from the block download logic by removing fRequested
    // This requires some new chain data structure to efficiently look up if a
    // block is in a chain leading to a candidate for best tip, despite not
    // being such a candidate itself.

    // TODO: deal better with return value and error conditions for duplicate
    // and unrequested blocks.
    if (fAlreadyHave) return true;
    if (!fRequested) {  // If we didn't ask for it:
        if (pindex->nTx != 0) return true;    // This is a previously-processed block that was pruned
        if (!fHasMoreOrSameWork) return true; // Don't process less-work chains
        if (fTooFarAhead) return true;        // Block height is too high

        // Protect against DoS attacks from low-work chains.
        // If our tip is behind, a peer could try to send us
        // low-work blocks on a fake chain that we would never
        // request; don't process these.
        if (pindex->nChainWork < nMinimumChainWork) return true;
    }

    // 2. 再一次檢查區(qū)塊
    if (!CheckBlock(block, state, m_params.GetConsensus()) ||
        !ContextualCheckBlock(block, state, m_params.GetConsensus(), pindex->pprev)) {
        if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
            pindex->nStatus |= BLOCK_FAILED_VALID;
            setDirtyBlockIndex.insert(pindex);
        }
        return error("%s: %s", __func__, state.ToString());
    }

    // 3. 如果這個區(qū)塊延續(xù)在當前的最佳鏈上,則調(diào)用src\net_processing.cpp
    // 中的PeerManagerImpl::NewPoWValidBlock函數(shù)廣播這個區(qū)塊
    // Header is valid/has work, merkle tree and segwit merkle tree are good...RELAY NOW
    // (but if it does not build on our best tip, let the SendMessages loop relay it)
    if (!IsInitialBlockDownload() && m_chain.Tip() == pindex->pprev)
        GetMainSignals().NewPoWValidBlock(pindex, pblock);

    // 4. 將這個區(qū)塊寫入到磁盤中
    // Write block to history file
    if (fNewBlock) *fNewBlock = true;
    try {
        FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, m_params, dbp);
        if (blockPos.IsNull()) {
            state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
            return false;
        }
        ReceivedBlockTransactions(block, pindex, blockPos);
    } catch (const std::runtime_error& e) {
        return AbortNode(state, std::string("System error: ") + e.what());
    }

    FlushStateToDisk(state, FlushStateMode::NONE);

    CheckBlockIndex();

    return true;
}

ActivateBestChain

src\validation.cpp中的CChainState::ActivateBestChain函數(shù),更新當前鏈為最長鏈。

bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr<const CBlock> pblock)
{
    // Note that while we're often called here from ProcessNewBlock, this is
    // far from a guarantee. Things in the P2P/RPC will often end up calling
    // us in the middle of ProcessNewBlock - do not assume pblock is set
    // sanely for performance or correctness!
    AssertLockNotHeld(cs_main);

    // ABC maintains a fair degree of expensive-to-calculate internal state
    // because this function periodically releases cs_main so that it does not lock up other threads for too long
    // during large connects - and to allow for e.g. the callback queue to drain
    // we use m_cs_chainstate to enforce mutual exclusion so that only one caller may execute this function at a time
    LOCK(m_cs_chainstate);

    CBlockIndex *pindexMostWork = nullptr;
    CBlockIndex *pindexNewTip = nullptr;
    int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
    do {
        // Block until the validation queue drains. This should largely
        // never happen in normal operation, however may happen during
        // reindex, causing memory blowup if we run too far ahead.
        // Note that if a validationinterface callback ends up calling
        // ActivateBestChain this may lead to a deadlock! We should
        // probably have a DEBUG_LOCKORDER test for this in the future.
        LimitValidationInterfaceQueue();

        {
            LOCK(cs_main);
            // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
            LOCK(MempoolMutex());
            CBlockIndex* starting_tip = m_chain.Tip();
            bool blocks_connected = false;
            do {
                // We absolutely may not unlock cs_main until we've made forward progress
                // (with the exception of shutdown due to hardware issues, low disk space, etc).
                ConnectTrace connectTrace; // Destructed before cs_main is unlocked

                // 1. 獲取最長鏈(最大工作量證明),刪除不合法的候選鏈
                if (pindexMostWork == nullptr) {
                    pindexMostWork = FindMostWorkChain();
                }

                // Whether we have anything to do at all.
                if (pindexMostWork == nullptr || pindexMostWork == m_chain.Tip()) {
                    break;
                }

                // 2. 更新當前鏈為最長的鏈
                bool fInvalidFound = false;
                std::shared_ptr<const CBlock> nullBlockPtr;
                if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) {
                    // A system error occurred
                    return false;
                }
                blocks_connected = true;

                if (fInvalidFound) {
                    // Wipe cache, we may need another branch now.
                    pindexMostWork = nullptr;
                }
                pindexNewTip = m_chain.Tip();

                // 3. 通知各監(jiān)聽器當前鏈發(fā)生變化
                for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
                    assert(trace.pblock && trace.pindex);
                    GetMainSignals().BlockConnected(trace.pblock, trace.pindex);
                }
            } while (!m_chain.Tip() || (starting_tip && CBlockIndexWorkComparator()(m_chain.Tip(), starting_tip)));
            if (!blocks_connected) return true;

            const CBlockIndex* pindexFork = m_chain.FindFork(starting_tip);
            bool fInitialDownload = IsInitialBlockDownload();

            // 4. 將最長鏈的高度和新區(qū)塊告訴其它節(jié)點
            // Notify external listeners about the new tip.
            // Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected
            if (pindexFork != pindexNewTip) {
                // Notify ValidationInterface subscribers
                GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);

                // Always notify the UI if a new block tip was connected
                uiInterface.NotifyBlockTip(GetSynchronizationState(fInitialDownload), pindexNewTip);
            }
        }
        // When we reach this point, we switched to a new tip (stored in pindexNewTip).

        if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown();

        // We check shutdown only after giving ActivateBestChainStep a chance to run once so that we
        // never shutdown before connecting the genesis block during LoadChainTip(). Previously this
        // caused an assert() failure during shutdown in such cases as the UTXO DB flushing checks
        // that the best block hash is non-null.
        if (ShutdownRequested()) break;
    } while (pindexNewTip != pindexMostWork);
    CheckBlockIndex();

    // Write changes periodically to disk, after relay.
    if (!FlushStateToDisk(state, FlushStateMode::PERIODIC)) {
        return false;
    }

    return true;
}

選擇最長鏈

在處理區(qū)塊中所調(diào)用的CChainState::ActivateBestChain函數(shù),負責更新當前鏈為最長鏈。在其過程中,會調(diào)用src\validation.cpp中的CChainState::FindMostWorkChain函數(shù)獲取最長鏈。

/**
 * Return the tip of the chain with the most work in it, that isn't
 * known to be invalid (it's however far from certain to be valid).
 */
CBlockIndex* CChainState::FindMostWorkChain() {
    do {
        CBlockIndex *pindexNew = nullptr;

        // 從后往前遍歷候選鏈
        // setBlockIndexCandidates是候選鏈的集合,且按照一定的規(guī)則排序,越符合要求的鏈在越后面
        // Find the best candidate header.
        {
            std::set<CBlockIndex*, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexCandidates.rbegin();
            if (it == setBlockIndexCandidates.rend())
                return nullptr;
            pindexNew = *it;
        }

        // 遍歷鏈上的區(qū)塊,檢查是否存在不合法的區(qū)塊
        // Check whether all blocks on the path between the currently active chain and the candidate are valid.
        // Just going until the active chain is an optimization, as we know all blocks in it are valid already.
        CBlockIndex *pindexTest = pindexNew;
        bool fInvalidAncestor = false;
        while (pindexTest && !m_chain.Contains(pindexTest)) {
            assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0);

            // Pruned nodes may have entries in setBlockIndexCandidates for
            // which block files have been deleted.  Remove those as candidates
            // for the most work chain if we come across them; we can't switch
            // to a chain unless we have all the non-active-chain parent blocks.
            bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_MASK;
            bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA);
            // 若存在不合法的區(qū)塊,則刪除該鏈
            if (fFailedChain || fMissingData) {
                // Candidate chain is not usable (either invalid or missing data)
                if (fFailedChain && (pindexBestInvalid == nullptr || pindexNew->nChainWork > pindexBestInvalid->nChainWork))
                    pindexBestInvalid = pindexNew;
                CBlockIndex *pindexFailed = pindexNew;
                // Remove the entire chain from the set.
                while (pindexTest != pindexFailed) {
                    if (fFailedChain) {
                        pindexFailed->nStatus |= BLOCK_FAILED_CHILD;
                    } else if (fMissingData) {
                        // If we're missing data, then add back to m_blocks_unlinked,
                        // so that if the block arrives in the future we can try adding
                        // to setBlockIndexCandidates again.
                        m_blockman.m_blocks_unlinked.insert(
                            std::make_pair(pindexFailed->pprev, pindexFailed));
                    }
                    setBlockIndexCandidates.erase(pindexFailed);
                    pindexFailed = pindexFailed->pprev;
                }
                setBlockIndexCandidates.erase(pindexTest);
                fInvalidAncestor = true;
                break;
            }
            pindexTest = pindexTest->pprev;
        }
        // 若不存在違法區(qū)塊,則返回該鏈
        if (!fInvalidAncestor)
            return pindexNew;
    } while(true);
}

FindMostWorkChain函數(shù)所在的CChainState類中包含一個屬性:std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates候選鏈集合,該集合的排序規(guī)則定義如下:

bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
    // First sort by most total work, ...
    if (pa->nChainWork > pb->nChainWork) return false;
    if (pa->nChainWork < pb->nChainWork) return true;

    // ... then by earliest time received, ...
    if (pa->nSequenceId < pb->nSequenceId) return false;
    if (pa->nSequenceId > pb->nSequenceId) return true;

    // Use pointer address as tie breaker (should only happen with blocks
    // loaded from disk, as those all have id 0).
    if (pa < pb) return false;
    if (pa > pb) return true;

    // Identical blocks.
    return false;
}

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容