title: 比特幣源碼分析——共識模塊
date: 2021-11-02 18:39:52
前言
分析比特幣系統(tǒng)的共識模塊,包括共識算法、交易的流程等,基于比特幣開源客戶端Bitcoin Core v22.0 版本的源碼。
比特幣中共識算法的大體步驟如下:
新交易被創(chuàng)建并廣播到比特幣網(wǎng)絡中。
每個節(jié)點接收到交易后,獨立地對交易進行驗證。
礦工節(jié)點將新交易收集到一個區(qū)塊中,并為該區(qū)塊尋找工作量證明,然后將新區(qū)塊廣播到網(wǎng)絡中。
每個節(jié)點收到區(qū)塊后,對區(qū)塊進行獨立的校驗,并組裝進區(qū)塊鏈中。
每個節(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)系圖如下:

創(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::ThreadMessageHandler在src\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ù)中:
- 首先,會調(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;
}
- 這筆被接受的交易不能是鑄幣交易:
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
return state.Invalid(TxValidationResult::TX_CONSENSUS, "coinbase");
- 檢查是否為標準交易,調(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);
- 交易的字節(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");
- 只接受
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");
- 檢查這筆交易是否已存在交易池中,或有相同未認證的數(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");
}
- 檢查交易輸入所指向的上一筆交易輸出
prevout是否與交易池中某筆交易的一樣,即防止雙花。
// Check for conflicts with in-memory transactions
for (const CTxIn &txin : tx.vin)
{
const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
if (ptxConflicting) {
......
}
}
- 檢查交易所有輸入的來源(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)) {
......
}
}
- 檢查時間鎖
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");
- 調(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;
}
- 檢查輸入和輸入的見證腳本是否符合標準(解鎖腳本能否解開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");
- 交易中的簽名數(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.cpp的generatetoaddress函數(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;
}