title: 比特币源码分析——共识模块
date: 2021-11-02 18:39:52
前言
分析比特币系统的共识模块,包括共识算法、交易的流程等,基于比特币开源客户端Bitcoin Core v22.0 版本的源码。
比特币中共识算法的大体步骤如下:
新交易被创建并广播到比特币网络中。
每个节点接收到交易后,独立地对交易进行验证。
矿工节点将新交易收集到一个区块中,并为该区块寻找工作量证明,然后将新区块广播到网络中。
每个节点收到区块后,对区块进行独立的校验,并组装进区块链中。
每个节点对区块链进行独立选择,选择最大工作量证明的链。
接下来,将结合源码讲解分析上述步骤。
创建交易
在Bitcoin Core客户端中,用户可以通过 $ bitcoin-cli createrawtransaction
命令创建一笔交易。这个命令背后对应着一个RPC接口,并映射到一个处理函数。映射关系定义在src\rpc\rawtransaction.cpp
中:
// 注册交易的RPC接口及其对应的处理函数
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);
}
}
该注册函数的被调用关系图如下:
创建交易RPC接口对应的处理函数如下:
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
{
// 检查四个参数 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();
}
// 构造交易(包括交易的输入vin、输出vout及锁定脚本),返回序列化数据
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
// 哈希
return EncodeHexTx(CTransaction(rawTx));
},
};
}
函数定义了命令的说明、请求参数、返回值与内部实现过程,主要是根据请求参数构造一笔交易,然后返回交易的哈希值。构造的过程主要包括构造交易的输入vin、输出vout及锁定脚本 ,具体内容不在此讲解。
构建交易之后,需要使用$ bitcoin-cli signrawtransactionwithkey
对交易进行签名,其处理函数也在src\rpc\rawtransaction.cpp
中,主要内容是构建vin中的解锁脚本 。
签名后,就可以使用$ bitcoin-cli sendrawtransaction
将交易广播至比特币网络中,具体实现代码不在此展示。
验证交易
Bitcoin Core客户端可通过bitcoind
命令启动,启动的入口函数是src\bitcoind.cpp
中的main
函数。启动之后,Bitcoin Core会启动一个线程用于监听、接收并响应比特币网络中的信息,该线程对应的函数CConnman::ThreadMessageHandler
在src\net.cpp
中。
该函数会调用src\net_processing.cpp
中的PeerManagerImpl::ProcessMessages
函数处理接收到的信息,而它会进一步调用同在src\net_processing.cpp
中的PeerManagerImpl::ProcessMessage
函数。其被调用的关系如下:
main -> AppInit -> AppInitMain -> CConnman::Start -> CConnman::ThreadMessageHandler
-> PeerManagerImpl::ProcessMessages -> PeerManagerImpl::ProcessMessage
在PeerManagerImpl::ProcessMessage
函数中,会根据信息类型进行不同的处理,如果是交易类型的消息,则会调用src\validation.cpp
中的AcceptToMemoryPool
函数将交易存放到交易池中。
void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
const std::chrono::microseconds time_received,
const std::atomic& interruptMsgProc)
{
......
// 如果消息是交易类型
if (msg_type == NetMsgType::TX) {
......
// 接收到交易池中
const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, ptx, false /* bypass_limits */);
......
}
......
}
AcceptToMemoryPool
函数进而调用AcceptToMemoryPoolWithTime
函数,接着调用MemPoolAccept:: AcceptSingleTransaction
函数。
而这个MemPoolAccept::AcceptSingleTransaction
函数,是接收交易的入口函数。在这个函数中,又会调用MemPoolAccept::PreChecks
函数,它是关键中的关键,负责对交易进行全方位的检查。在这个函数中:
- 首先,会调用
src\consensus\tx_check.cpp
中的CheckTransaction
函数,进行基础的检查:
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. 检查输出值不能为负数或者超过范围
// 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 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");
- 检查是否为标准交易,调用
src\policy\policy.cpp
中的IsStandardTx
函数,检查交易的版本、大小、脚本、输出中UTXO个数等是否符合标准。
std::string reason;
if (fRequireStandard && !IsStandardTx(tx, reason))
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason);
- 交易的字节大小不能太小。
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
满足要求、能够被打包进下一个被挖区块中的交易,防止交易过多溢出交易池。
// 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");
- 检查这笔交易是否已存在交易池中,或有相同未认证的数据已在交易池中。
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
是否满足要求,即可以被打包进下一个待挖区块中,不然就丢弃。
// 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");
- 调用
src\consensus\tx_verify.cpp
中的Consensus::CheckTxInputs
函数检查交易的输入。
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,检查是否成熟(确认数不小于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. 检查每一个输入值和总值是否在限定范围内
// 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. 交易的矿工费用需在规定范围内
// 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");
- 交易中的签名数量(sigops)应小于签名操作数量上限。
if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
strprintf("%d", nSigOpsCost));
创建区块
在Bitcoin Core中,可通过$ bitcoin-cli generatetoaddress
命令进行挖矿,命令内部实现定义在src\rpc\mining.cpp
的generatetoaddress
函数中:
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
{
// 要创建区块的个数
const int num_blocks{request.params[0].get_int()};
// 尝试次数
const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
// 创建区块所获得的coinbase奖励转入的地址
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);
// 开始创建区块
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
},
};
}
进行一系列检查后,它将调用同在src\rpc\mining.cpp
中的generateBlocks
函数,它是创建区块的入口函数:
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);
// 区块链当前高度
nHeight = chainman.ActiveChain().Height();
// 创建nGenerate个区块后的高度
nHeightEnd = nHeight+nGenerate;
}
unsigned int nExtraNonce = 0;
UniValue blockHashes(UniValue::VARR);
// 开始构造区块并挖矿
while (nHeight < nHeightEnd && !ShutdownRequested())
{
// 调用 BlockAssembler::CreateNewBlock 构造候选区块
// chainman.ActiveChainstate()会获取当前最长链,新区块将会基于最长链延续
std::unique_ptr 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;
// 调用GenerateBlock进行挖矿:遍历区块的nonce值,使得区块哈希值满足工作量证明
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;
}
该函数调用了两个关键函数:
CreateNewBlock
src\miner.cpp
中的BlockAssembler::CreateNewBlock
函数,用于构造候选区块:
std::unique_ptr 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. 区块中的第一个位置留给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. 取当前区块链的最后一个节点,作为新区块的父区块
CBlockIndex* pindexPrev = m_chainstate.m_chain.Tip();
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
// 3. 计算区块版本
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. 从交易池中选择一批交易打包到区块中(注意:并不会从交易持中将交易删除,删除需要等区块确认以后)
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;
// 矿工奖励=区块中交易费用总和(在addPackageTxs时会进行统计)+系统发放奖励
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. 填充区块头
// 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]);
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
函数,用于挖矿:
static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t& max_tries, unsigned int& extra_nonce, uint256& block_hash)
{
block_hash.SetNull();
{
LOCK(cs_main);
// 在这个函数中会调用src\consensus\merkle.cpp中的BlockMerkleRoot函数
// 计算区块MerkleRoot的值
IncrementExtraNonce(&block, chainman.ActiveChain().Tip(), extra_nonce);
}
CChainParams chainparams(Params());
// 遍历nonce值寻找工作量证明。
// 调用src\pow.cpp中的CheckProofOfWork函数检查是否满足工作量证明,即区块哈希小于目标值
while (max_tries > 0 && block.nNonce < std::numeric_limits::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::max()) {
return true;
}
// 调用src\validation.cpp中的ChainstateManager::ProcessNewBlock函数
// 处理新区块:验证区块、接收区块(链接到对应链上、广播到网络中等)、更新最长链
std::shared_ptr shared_pblock = std::make_shared(block);
if (!chainman.ProcessNewBlock(chainparams, shared_pblock, true, nullptr)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
}
block_hash = block.GetHash();
return true;
}
处理区块
书接上文,创建区块之后,会调用src\validation.cpp
中的ChainstateManager::ProcessNewBlock
函数处理新区块。
同样,在接收到其它节点传播来的区块信息后,也会调用这个函数。“验证交易”一节中提到src\net_processing.cpp
中的PeerManagerImpl::ProcessMessage
函数用于处理从网络中接收到的信息,如果信息是区块类型,则会调用PeerManagerImpl::ProcessBlock
函数。
void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
const std::chrono::microseconds time_received,
const std::atomic& interruptMsgProc)
{
......
if (msg_type == NetMsgType::BLOCK)
{
......
ProcessBlock(pfrom, pblock, forceProcessing);
return;
}
......
}
而PeerManagerImpl::ProcessBlock
函数则是直接调用ChainstateManager::ProcessNewBlock
函数。
void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptr& 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
函数是处理新区块的入口函数/关键函数,它将主要负责:验证区块、接收区块、更新最长链。
bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr& 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. 验证区块
// 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. 接收区块
// 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;
}
它主要调用如下三个函数。
CheckBlock
src\validation.cpp
中的CheckBlock
函数,负责对区块进行检查:
// fCheckPOW和fCheckMerkleRoot两个参数的默认值是true
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot)
{
// These are checks that are independent of context.
// 如果区块已验证,则直接返回
if (block.fChecked)
return true;
// 1. 检查区块头,是否满足工作量证明
// 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. 检查区块大小是否在规定范围内
// 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. 调用src\consensus\tx_check.cpp中的CheckTransaction函数(上文已介绍)检查每笔交易是否正确,
// 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个数不能超过限定值
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
函数,用于接收区块,负责基本的验证、链接到对应链上、广播到网络中、保存到本地磁盘等。
/** 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& 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. 接收区块头:
// 检查是否有重复区块头、是否存在父区块、是否延续于不合法区块之后(分叉情况)
// 将区块头链接到对应的链上(可能会出现分叉情况)
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. 再一次检查区块
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. 如果这个区块延续在当前的最佳链上,则调用src\net_processing.cpp
// 中的PeerManagerImpl::NewPoWValidBlock函数广播这个区块
// 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. 将这个区块写入到磁盘中
// 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
函数,更新当前链为最长链。
bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr 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 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. 通知各监听器当前链发生变化
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. 将最长链的高度和新区块告诉其它节点
// 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;
}
选择最长链
在处理区块中所调用的CChainState::ActivateBestChain
函数,负责更新当前链为最长链。在其过程中,会调用src\validation.cpp
中的CChainState::FindMostWorkChain
函数获取最长链。
/**
* 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是候选链的集合,且按照一定的规则排序,越符合要求的链在越后面
// Find the best candidate header.
{
std::set::reverse_iterator it = setBlockIndexCandidates.rbegin();
if (it == setBlockIndexCandidates.rend())
return nullptr;
pindexNew = *it;
}
// 遍历链上的区块,检查是否存在不合法的区块
// 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);
// 若存在不合法的区块,则删除该链
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;
}
// 若不存在违法区块,则返回该链
if (!fInvalidAncestor)
return pindexNew;
} while(true);
}
FindMostWorkChain
函数所在的CChainState
类中包含一个属性:std::set
候选链集合,该集合的排序规则定义如下:
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;
}
参考
Bitcoin Core: Main Page
Introduction - 《《精通比特币》第二版》 - 书栈网 · BookStack
https://juejin.cn/post/6844903598522892302