在阅读本文之前,推荐先阅读我的上一篇博文-<比特币源码情景分析之消息协议>, 该博文转发自消息协议的wiki
比特币节点之间传递数据都是以message格式化的数据传输的。且比特币节点的大部分活动都是在处理message,发送message,因此分析这些消息即可了解区块链的大部分机制。处理message的线程入口函数是
ThreadMessageHandler,在初始化时创建
bool AppInitMain()
{
if (!connman.Start(scheduler, connOptions)) {
return false;
}
}
bool CConnman::
Start
(CScheduler& scheduler, const Options& connOptions)
{
Init(connOptions);
// Send and receive from sockets, accept connections
threadSocketHandler = std::thread(&TraceThread >, "net", std::function(std::bind(&CConnman::ThreadSocketHandler, this)));
// Process messages
threadMessageHandler = std::thread(&TraceThread >, "msghand", std::function(std::bind(&CConnman::ThreadMessageHandler, this)));
return true;
}
void CConnman::
ThreadMessageHandler
()
{
while (!flagInterruptMsgProc)
{
bool fMoreWork = false;
for (CNode* pnode : vNodesCopy)
{
if (pnode->fDisconnect)
continue;
// Receive messages
//接收消息并处理
bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, flagInterruptMsgProc);
fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend);
if (flagInterruptMsgProc)
return;
// Send messages
{
LOCK(pnode->cs_sendProcessing);
//发送消息
m_msgproc->SendMessages(pnode, flagInterruptMsgProc);
}
if (flagInterruptMsgProc)
return;
}
if (!fMoreWork) {
//等待新的网络数据,比如有新消息
condMsgProc
.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return
fMsgProcWake
; });
}
fMsgProcWake = false;
}
}
该线程处理完数据后就进入等待,直接到有新的网络事件由threadSocketHandler唤醒
void CConnman::WakeMessageHandler()
{
{
std::lock_guard lock(mutexMsgProc);
fMsgProcWake = true;
}
condMsgProc.notify_one();
}
void CConnman::ThreadSocketHandler()
{
if (nBytes > 0)
{
bool notify = false;
if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))
pnode->CloseSocketDisconnect();
RecordBytesRecv(nBytes);
if (notify) {
....
WakeMessageHandler();
}
}
}
为了更好的描述出区块message的规则及数据流,下面将以获取block数据为例来分析,获取block数据有两种场景,一种是主动,一种是被动被通知
被动获取新block
Peer节点从其他节点获取到新block时,会通过INV(MSG_BLOCK)通知Local节点有新block,具体有如下几种CASE
bool PeerLogicValidation::
ProcessMessages
(CNode* pfrom, std::atomic& interruptMsgProc)
{
else if (strCommand == NetMsgType::
CMPCTBLOCK
&& !fImporting && !fReindex) // Ignore blocks received while importing
{
….
if (fBlockReconstructed) {
// If we got here, we were able to optimistically reconstruct a
// block that is in flight from some other peer.
{
LOCK(cs_main);
mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom->GetId(), false));
}
bool fNewBlock = false;
ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
}
else if (strCommand == NetMsgType::
BLOCKTXN
&& !fImporting && !fReindex) // Ignore blocks received while importing
{
BlockTransactions resp;
vRecv >> resp;
if (fBlockRead) {
bool fNewBlock = false;
// Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
// even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
// This bypasses some anti-DoS logic in AcceptBlock (eg to prevent
// disk-space attacks), but this should be safe due to the
// protections in the compact block handler -- see related comment
// in compact block optimistic reconstruction handling.
ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
}
}
else if (strCommand == NetMsgType::
BLOCK
&& !fImporting && !fReindex) // Ignore blocks received while importing
{
std::shared_ptr pblock = std::make_shared();
vRecv >> *pblock;
bool forceProcessing = false;
const uint256 hash(pblock->GetHash());
bool fNewBlock = false;
ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock);
}
}
bool
ProcessNewBlock
(const CChainParams& chainparams, const std::shared_ptr pblock, bool fForceProcessing, bool *fNewBlock)
{
AssertLockNotHeld(cs_main);
{
CBlockIndex *pindex = nullptr;
if (fNewBlock) *fNewBlock = false;
CValidationState state;
// Ensure that CheckBlock() passes before calling AcceptBlock, as
// belt-and-suspenders.
bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
LOCK(cs_main);
if (ret) {
// Store to disk
ret = g_chainstate.AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
}
if (!ret) {
GetMainSignals().BlockChecked(*pblock, state);
return error("%s: AcceptBlock FAILED (%s)", __func__, state.GetDebugMessage());
}
}
NotifyHeaderTip();
CValidationState state; // Only used to report errors, not invalidity - ignore it
if (!g_chainstate.ActivateBestChain(state, chainparams, pblock))
return error("%s: ActivateBestChain failed", __func__);
return true;
}
bool CChainState::
ActivateBestChain
(CValidationState &state, const CChainParams& chainparams, 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);
CBlockIndex *pindexMostWork = nullptr;
CBlockIndex *pindexNewTip = nullptr;
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
do {
...
// Notify external listeners about the new tip.
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
} while (pindexNewTip != pindexMostWork);
return true;
}
void PeerLogicValidation::
UpdatedBlockTip
(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
const int nNewHeight = pindexNew->nHeight;
connman->SetBestHeight(nNewHeight);
SetServiceFlagsIBDCache(!fInitialDownload);
if (!fInitialDownload) {
// Find the hashes of all blocks that weren't previously in the best chain.
std::vector vHashes;
const CBlockIndex *pindexToAnnounce = pindexNew;
while (pindexToAnnounce != pindexFork) {
vHashes.push_back(pindexToAnnounce->GetBlockHash());
pindexToAnnounce = pindexToAnnounce->pprev;
if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) {
// Limit announcements in case of a huge reorganization.
// Rely on the peer's synchronization mechanism in that case.
break;
}
}
// Relay inventory, but don't relay old inventory during initial block download.
connman->ForEachNode([nNewHeight, &vHashes](CNode* pnode) {
if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) {
for (const uint256& hash : reverse_iterate(vHashes)) {
pnode->PushBlockHash(hash);
}
}
});
connman->WakeMessageHandler();
}
nTimeBestReceived = GetTime();
}
一个节点挖出一个新块时也会调用processNewBlock然后发出INV(MSG_BLOCK)消息
UniValue
generateBlocks
(std::shared_ptr coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
{
static const int nInnerLoopCount = 0x10000;
int nHeightEnd = 0;
int nHeight = 0;
{ // Don't keep cs_main locked
LOCK(cs_main);
nHeight = chainActive.Height();
nHeightEnd = nHeight+nGenerate;
}
unsigned int nExtraNonce = 0;
UniValue blockHashes(UniValue::VARR);
while (nHeight < nHeightEnd)
{
std::unique_ptr pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript));
if (!pblocktemplate.get())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
std::shared_ptr shared_pblock = std::make_shared(*pblock);
if (!ProcessNewBlock(Params(), shared_pblock, true, nullptr))
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
}
return blockHashes;
}
主动获取新BLOCK
区块初始化时本地只有generis block,需要主动循环获取所有block信息
bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic& interruptMsgProc)
{
// Start block sync
if (pindexBestHeader == nullptr)
pindexBestHeader = chainActive.Tip();
bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do.
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
state.nHeadersSyncTimeout = GetTimeMicros() + HEADERS_DOWNLOAD_TIMEOUT_BASE + HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER * (GetAdjustedTime() - pindexBestHeader->GetBlockTime())/(consensusParams.nPowTargetSpacing);
nSyncStarted++;
const CBlockIndex *pindexStart = pindexBestHeader;
/* If possible, start at the block preceding the currently
best known header. This ensures that we always get a
non-empty list of headers back as long as the peer
is up-to-date. With a non-empty response, we can initialise
the peer's known best block. This wouldn't be possible
if we requested starting at pindexBestHeader and
got back an empty response. */
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), pto->nStartingHeight);
connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()));
}
}
}
getBlocks和getheaders时会传递locator,用来和outbound peer比对起始block
block数据获取流程
由于主动和被动获取block流程中的共同部分是GETHEADERS->HEADERS->GETDATA->BLOCK,该章节只从GETHEADERS消息讲起.
1) Peer 节点处理GETHEADERS
处理逻辑如下
else if (strCommand == NetMsgType::GETHEADERS)
{
CBlockLocator locator;
uint256 hashStop;
vRecv >> locator >> hashStop;
LOCK(cs_main);
//节点还处于初始化阶段,自然没有资源服务其他节点
if (
IsInitialBlockDownload
() && !pfrom->fWhitelisted) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->GetId());
return true;
}
CNodeState *nodestate = State(pfrom->GetId());
const CBlockIndex* pindex = nullptr;
if (locator.IsNull())
{
// If locator is null, return the hashStop block
pindex = LookupBlockIndex(hashStop);
if (!pindex) {
return true;
}
if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) {
LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom->GetId());
return true;
}
}
else
{
// Find the last block the caller has in the main chain
//根据locator的信息,发现该节点和from节点区块链表相同的部分,并从相同部分开始发送headers
pindex = FindForkInGlobalIndex(chainActive, locator);
if (pindex)
pindex = chainActive.Next(pindex);
}
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
std::vector vHeaders;
int nLimit = MAX_HEADERS_RESULTS;
LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->GetId());
for (; pindex; pindex = chainActive.Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
// pindex can be nullptr either if we sent chainActive.Tip() OR
// if our peer has chainActive.Tip() (and thus we are sending an empty
// headers message). In both cases it's safe to update
// pindexBestHeaderSent to be our tip.
//
// It is important that we simply reset the BestHeaderSent value here,
// and not max(BestHeaderSent, newHeaderSent). We might have announced
// the currently-being-connected tip using a compact block, which
// resulted in the peer sending a headers request, which we respond to
// without the new block. By resetting the BestHeaderSent, we ensure we
// will re-announce the new block via headers (or compact blocks again)
// in the SendMessages logic.
nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip();
//将blockheader发送回pfrom
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
}
2) local节点收到HEADERS消息
else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing
{
std::vector headers;
// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
unsigned int nCount = ReadCompactSize(vRecv);
if (nCount > MAX_HEADERS_RESULTS) {
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 20, strprintf("headers message size = %u", nCount));
return false;
}
headers.resize(nCount);
for (unsigned int n = 0; n < nCount; n++) {
vRecv >> headers[n];
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
}
// Headers received via a HEADERS message should be valid, and reflect
// the chain the peer is on. If we receive a known-invalid header,
// disconnect the peer if it is using one of our outbound connection
// slots.
bool should_punish = !pfrom->fInbound && !pfrom->m_manual_connection;
return ProcessHeadersMessage(pfrom, connman, headers, chainparams, should_punish);
}
bool static
ProcessHeadersMessage
(CNode *pfrom, CConnman *connman, const std::vector& headers, const CChainParams& chainparams, bool punish_duplicate_invalid)
{
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
size_t nCount = headers.size();
if (nCount == 0) {
// Nothing interesting. Stop asking this peers for more headers.
return true;
}
bool received_new_header = false;
const CBlockIndex *pindexLast = nullptr;
{
LOCK(cs_main);
CNodeState *nodestate = State(pfrom->GetId());
// If this looks like it could be a block announcement (nCount <
// MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that
// don't connect:
// - Send a getheaders message in response to try to connect the chain.
// - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that
// don't connect before giving DoS points
// - Once a headers message is received that is valid and does connect,
// nUnconnectingHeaders gets reset back to 0.
if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
//收到的header和我们本地的tip区块不连续,说明缺少部分Headers,需要GETHEADERS
//这个分支应该不是我们主动调用GETHEADERS的返回值
nodestate->nUnconnectingHeaders++;
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
return true;
}
uint256 hashLastBlock;
for (const CBlockHeader& header : headers) {
//我们主动请求GETHEADERS,但是对方缺返回不连续的HEADERS, 标记为捣乱节点
if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
Misbehaving(pfrom->GetId(), 20, "non-continuous headers sequence");
return false;
}
hashLastBlock = header.GetHash();
}
// If we don't have the last header, then they'll have given us
// something new (if these headers are valid).
//如果不存在说明有新的block
if (!LookupBlockIndex(hashLastBlock)) {
received_new_header = true;
}
}
CValidationState state;
CBlockHeader first_invalid_header;
//ProcessNewBlockHeaders会生成本地区块对象CBlockIndex,并保存到数据库中
if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) {
…..
}
{
LOCK(cs_main);
CNodeState *nodestate = State(pfrom->GetId());
if (nodestate->nUnconnectingHeaders > 0) {
LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders);
}
nodestate->nUnconnectingHeaders = 0;
assert(pindexLast);
UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
// From here, pindexBestKnownBlock should be guaranteed to be non-null,
// because it is set in UpdateBlockAvailability. Some nullptr checks
// are still present, however, as belt-and-suspenders.
if (received_new_header && pindexLast->nChainWork > chainActive.Tip()->nChainWork) {
nodestate->m_last_block_announcement = GetTime();
}
//如果返回的headers数据达到最大值,说明peer节点可能有更多headers,再次请求headers
if (nCount == MAX_HEADERS_RESULTS) {
// Headers message had its maximum size; the peer may have more headers.
// TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
// from there instead.
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight);
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()));
}
bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
// If this set of headers is valid and ends in a block with at least as
// much work as our tip, download as much as possible.
if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
std::vector vToFetch;
const CBlockIndex *pindexWalk = pindexLast;
// If pindexWalk still isn't on our main chain, we're looking at a
// very large reorg at a time we think we're close to caught up to
// the main chain -- this shouldn't really happen. Bail out on the
// direct fetch and rely on parallel download instead.
if (!chainActive.Contains(pindexWalk)) {
LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
pindexLast->GetBlockHash().ToString(),
pindexLast->nHeight);
} else {
std::vector vGetData;
// Download as much as possible, from earliest to latest.
//生成获取block数据的INV对象
for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) {
if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
// Can't download any more from this peer
break;
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()))
}
if (vGetData.size() > 0) {
if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
// In any case, we want to download using a compact block, not a regular one
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
}
//请求区块数据
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
}
}
}
}
return true;
}
bool
ProcessNewBlockHeaders
(const std::vector& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex, CBlockHeader *first_invalid)
{
if (first_invalid != nullptr) first_invalid->SetNull();
{
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
if (!g_chainstate.AcceptBlockHeader(header, state, chainparams, &pindex)) {
if (first_invalid) *first_invalid = header;
return false;
}
if (ppindex) {
*ppindex = pindex;
}
}
}
NotifyHeaderTip();
return true;
}
bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex)
{
AssertLockHeld(cs_main);
// Check for duplicate
uint256 hash = block.GetHash();
BlockMap::iterator miSelf = mapBlockIndex.find(hash);
CBlockIndex *pindex = nullptr;
if (hash != chainparams.GetConsensus().hashGenesisBlock) {
if (miSelf != mapBlockIndex.end()) {
//已经存在不处理
// Block header is already known.
pindex = miSelf->second;
if (ppindex)
*ppindex = pindex;
if (pindex->nStatus & BLOCK_FAILED_MASK)
return state.Invalid(error("%s: block %s is marked invalid", __func__, hash.ToString()), 0, "duplicate");
return true;
}
//验证区块头是否合法,其实就是POW检测,是对单个区块的数据正确性检测
if (!CheckBlockHeader(block, state, chainparams.GetConsensus()))
return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
// Get prev block index
CBlockIndex* pindexPrev = nullptr;
//本blockHeader.prev必须已经存在,这样链条才是连续的
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
return state.DoS(10, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
pindexPrev = (*mi).second;
if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk”);
//ContextualCheckBlockHeader,为啥有个contextual呢,这个词用来这里很形象,这里检测得内容是和其他块相关的数据,比如这个块的难度系数是否正确,是否比prevBlock难,且符合难度动态调整规则
if (!
ContextualCheckBlockHeader
(block, state, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
}
if (pindex == nullptr)
//将该区块天际到mapBlockIndex中
pindex = AddToBlockIndex(block);
if (ppindex)
*ppindex = pindex;
//检测区块链的完整性
CheckBlockIndex(chainparams.GetConsensus());
return true;
}
static bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true)
{
// Check proof of work matches claimed amount
if (fCheckPOW && !
CheckProofOfWork
(block.GetHash(), block.nBits, consensusParams))
return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed");
return true;
}
static bool
ContextualCheckBlockHeader
(const CBlockHeader& block, CValidationState& state, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
{
// Check proof of work
const Consensus::Params& consensusParams = params.GetConsensus();
if (block.nBits != GetNextWorkRequired(
pindexPrev
, &
block
, consensusParams))
return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work");
}
3) peer节点收到GETDATA消息
else if (strCommand == NetMsgType::GETDATA)
{
std::vector vInv;
vRecv >> vInv;
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
ProcessGetData(pfrom, chainparams.GetConsensus(), connman, interruptMsgProc);
}
void static
ProcessGetData
(CNode* pfrom, const Consensus::Params& consensusParams, CConnman* connman, const std::atomic& interruptMsgProc)
{
AssertLockNotHeld(cs_main);
std::deque::iterator it = pfrom->vRecvGetData.begin();
std::vector vNotFound;
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
{
LOCK(cs_main);
if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) {
const CInv &inv = *it;
if (
inv.type == MSG_BLOCK
|| inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
it++;
ProcessGetBlockData(pfrom, consensusParams, inv, connman, interruptMsgProc);
}
}
}
void static
ProcessGetBlockData
(CNode* pfrom, const Consensus::Params& consensusParams, const CInv& inv, CConnman* connman, const std::atomic& interruptMsgProc)
{
LOCK(cs_main);
//根据block.hash从本地找到对应的CBlockIndex
const CBlockIndex* pindex = LookupBlockIndex(inv.hash);
if (pindex) {
send = BlockRequestAllowed(pindex, consensusParams);
if (!send) {
LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
}
}
// Pruned nodes may have deleted the block, so check whether
// it's available before trying to send.
if (send && (pindex->nStatus & BLOCK_HAVE_DATA))
{
std::shared_ptr pblock;
if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
pblock = a_recent_block;
} else {
// Send block from disk
std::shared_ptr pblockRead = std::make_shared();
//由于CBlockIndex不保存有block的交易数据,而是通过CBlockPos对象间接地保存了数据的信息
//这里需要根据block.CBlockPos从磁盘读取交易数据
if (!ReadBlockFromDisk(*pblockRead, pindex, consensusParams))
assert(!"cannot load block from disk");
pblock = pblockRead;
}
if (inv.type == MSG_BLOCK)
//返回数据
connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
} else ….
}
}
inline CBlockIndex*
LookupBlockIndex
(const uint256& hash)
{
AssertLockHeld(cs_main);
//从mapBlockIndex map中根据hash获取CBlockIndex
BlockMap::const_iterator it = mapBlockIndex.find(hash);
return it == mapBlockIndex.end() ? nullptr : it->second;
}
bool
ReadBlockFromDisk
(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
CDiskBlockPos blockPos;
{
LOCK(cs_main);
//CDiskBlockPos包含了区块数据存储文件及位置信息,很容易从文件中恢复出区块交易数据
blockPos = pindex->GetBlockPos();
}
if (!
ReadBlockFromDisk
(block,
blockPos
, consensusParams))
return false;
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
block.SetNull();
// Open history file to read
CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString());
// Read block
try {
filein >> block;
}
catch (const std::exception& e) {
return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString());
}
return true;
}
4.Local节点收到BLOCK消息
else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
{
std::shared_ptr pblock = std::make_shared();
vRecv >> *pblock;
bool fNewBlock = false;
ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock)
}
bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr
pblock
, bool fForceProcessing, bool *fNewBlock)
{
{
CBlockIndex *pindex = nullptr;
// Ensure that CheckBlock() passes before calling AcceptBlock, as
// belt-and-suspenders.
//验证区块交易数据是否正确完整
bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
LOCK(cs_main);
if (ret) {
// Store to disk
//保存到磁盘,前面保存CBlockIndex结构是通过leveldb存储的,而区块交易数据大,且不需要做map,英文采用文件存储更适合
ret = g_chainstate.AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
}
if (!ret) {
GetMainSignals().BlockChecked(*pblock, state);
return error("%s: AcceptBlock FAILED (%s)", __func__, state.GetDebugMessage());
}
}
//告知监听模块区块高度发生变化了
NotifyHeaderTip();
CValidationState state; // Only used to report errors, not invalidity - ignore it
//前面也提到过这个函数,这个函数会给其他节点发消息通知有本地节点有新的区块了
if (!g_chainstate.ActivateBestChain(state, chainparams, pblock))
return error("%s: ActivateBestChain failed", __func__);
return true;
}
//该函数的具体实现是将交易数据按照merkle tree的规则生成一个merkleroot,再和区块头中保存的merkle数据比较,如果一直,说明交易数据没有破坏
bool
CheckBlock
(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot)
{
// Check the merkle root.
if (
fCheckMerkleRoot
) {
bool mutated;
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
if (block.hashMerkleRoot != hashMerkleRoot2)
return state.DoS(100, false, REJECT_INVALID, "bad-txnmrklroot", true, "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.DoS(100, false, REJECT_INVALID, "bad-txns-duplicate", true, "duplicate transaction");
}
// First transaction must be coinbase, the rest must not be
//第一笔交易必须是矿工费用输出(CoinBase),其他txout不适矿工费用输出
if (block.vtx.empty() || !block.vtx[0]->IsCoinBase())
return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase");
for (unsigned int i = 1; i < block.vtx.size(); i++)
if (block.vtx[i]->IsCoinBase())
return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase");
// Check transactions
for (const auto& tx : block.vtx)
//验证交易是否合法,比如交易中的txin是否unspent,输出输入金额是否正确,签名脚本是否
if (!
CheckTransaction
(*tx, state, false))
return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(),
strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage()));
return true;
}
bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs)
{
// Basic checks that don't depend on any context
//txin,txout不能为空,value要满足输出=输入
if (tx.vin.empty())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty");
if (tx.vout.empty())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
// Check for negative or overflow output values
CAmount nValueOut = 0;
for (const auto& txout : tx.vout)
{
if (txout.nValue < 0)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
if (txout.nValue > MAX_MONEY)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
}
// Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
//不能有重复的txin
if (fCheckDuplicateInputs) {
std::set vInOutPoints;
for (const auto& txin : tx.vin)
{
if (!vInOutPoints.insert(txin.prevout).second)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
}
}
if (tx.IsCoinBase())
{
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
}
else
{
//每个txin必须要引用一个以前的unspent txout
for (const auto& txin : tx.vin)
if (txin.prevout.IsNull())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
}
return true;
}
bool CChainState::AcceptBlock(const std::shared_ptr& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock)
{
const CBlock& block = *pblock;
if (fNewBlock) *fNewBlock = false;
AssertLockHeld(cs_main);
CBlockIndex *pindexDummy = nullptr;
CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy;
//这里这个函数一般不会起什么作用,因为前面收到HEADERS消息时就会调用一次
if (!
AcceptBlockHeader
(block, state, chainparams, &pindex))
return false;
if (fNewBlock) *fNewBlock = true;
if (!CheckBlock(block, state, chainparams.GetConsensus()) ||
!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) {
if (state.IsInvalid() && !state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
}
return error("%s: %s", __func__, FormatStateMessage(state));
}
// 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() && chainActive.Tip() == pindex->pprev)
//通知监听模块发现了新的block
GetMainSignals().NewPoWValidBlock(pindex, pblock);
// Write block to history file
try {
//将区块交易数据写入磁盘
CDiskBlockPos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
if (blockPos.IsNull()) {
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
return false;
}
if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
return error("AcceptBlock(): ReceivedBlockTransactions failed");
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error: ") + e.what());
}
CheckBlockIndex(chainparams.GetConsensus());
return true;
}
//该函数负责将交易存储信息CDiskBlockPos赋值给CBlockIndex,并将block从mapBlocksUnlinked删除
bool CChainState::ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
//赋值CDiskBlockPos
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
pindexNew->nFile = pos.nFile;
pindexNew->nDataPos = pos.nPos;
pindexNew->nUndoPos = 0;
pindexNew->nStatus |= BLOCK_HAVE_DATA;
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
setDirtyBlockIndex.insert(pindexNew);
if (pindexNew->pprev == nullptr || pindexNew->pprev->nChainTx) {
// If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS.
std::deque queue;
queue.push_back(pindexNew);
// Recursively process any descendant blocks that now may be eligible to be connected.
while (!queue.empty()) {
CBlockIndex *pindex = queue.front();
queue.pop_front();
pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx;
{
LOCK(cs_nBlockSequenceId);
pindex->nSequenceId = nBlockSequenceId++;
}
if (chainActive.Tip() == nullptr || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) {
setBlockIndexCandidates.insert(pindex);
}
std::pair::iterator, std::multimap::iterator> range = mapBlocksUnlinked.equal_range(pindex);
while (range.first != range.second) {
std::multimap::iterator it = range.first;
queue.push_back(it->second);
range.first++;
mapBlocksUnlinked.erase(it);
}
}
} else {
if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) {
mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew));
}
}
return true;
}
/********************************
* 本文来自CSDN博主"爱踢门"
* 转载请标明出处
: http://blog.csdn.net/itleaks
******************************************/