Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 246 Lines • ▼ Show 20 Lines | struct CNodeState { | ||||
//! String name of this peer (debugging/logging purposes). | //! String name of this peer (debugging/logging purposes). | ||||
const std::string name; | const std::string name; | ||||
//! List of asynchronously-determined block rejections to notify this peer | //! List of asynchronously-determined block rejections to notify this peer | ||||
//! about. | //! about. | ||||
std::vector<CBlockReject> rejects; | std::vector<CBlockReject> rejects; | ||||
//! The best known block we know this peer has announced. | //! The best known block we know this peer has announced. | ||||
const CBlockIndex *pindexBestKnownBlock; | const CBlockIndex *pindexBestKnownBlock; | ||||
//! The hash of the last unknown block this peer has announced. | //! The hash of the last unknown block this peer has announced. | ||||
uint256 hashLastUnknownBlock; | BlockHash hashLastUnknownBlock; | ||||
//! The last full block we both have. | //! The last full block we both have. | ||||
const CBlockIndex *pindexLastCommonBlock; | const CBlockIndex *pindexLastCommonBlock; | ||||
//! The best header we have sent our peer. | //! The best header we have sent our peer. | ||||
const CBlockIndex *pindexBestHeaderSent; | const CBlockIndex *pindexBestHeaderSent; | ||||
//! Length of current-streak of unconnecting headers announcements | //! Length of current-streak of unconnecting headers announcements | ||||
int nUnconnectingHeaders; | int nUnconnectingHeaders; | ||||
//! Whether we've started headers synchronization with this peer. | //! Whether we've started headers synchronization with this peer. | ||||
bool fSyncStarted; | bool fSyncStarted; | ||||
▲ Show 20 Lines • Show All 127 Lines • ▼ Show 20 Lines | struct CNodeState { | ||||
TxDownloadState m_tx_download; | TxDownloadState m_tx_download; | ||||
CNodeState(CAddress addrIn, std::string addrNameIn) | CNodeState(CAddress addrIn, std::string addrNameIn) | ||||
: address(addrIn), name(addrNameIn) { | : address(addrIn), name(addrNameIn) { | ||||
fCurrentlyConnected = false; | fCurrentlyConnected = false; | ||||
nMisbehavior = 0; | nMisbehavior = 0; | ||||
fShouldBan = false; | fShouldBan = false; | ||||
pindexBestKnownBlock = nullptr; | pindexBestKnownBlock = nullptr; | ||||
hashLastUnknownBlock.SetNull(); | hashLastUnknownBlock = BlockHash(); | ||||
pindexLastCommonBlock = nullptr; | pindexLastCommonBlock = nullptr; | ||||
pindexBestHeaderSent = nullptr; | pindexBestHeaderSent = nullptr; | ||||
nUnconnectingHeaders = 0; | nUnconnectingHeaders = 0; | ||||
fSyncStarted = false; | fSyncStarted = false; | ||||
nHeadersSyncTimeout = 0; | nHeadersSyncTimeout = 0; | ||||
nStallingSince = 0; | nStallingSince = 0; | ||||
nDownloadingSince = 0; | nDownloadingSince = 0; | ||||
nBlocksInFlight = 0; | nBlocksInFlight = 0; | ||||
▲ Show 20 Lines • Show All 174 Lines • ▼ Show 20 Lines | if (!state->hashLastUnknownBlock.IsNull()) { | ||||
state->pindexBestKnownBlock = pindex; | state->pindexBestKnownBlock = pindex; | ||||
} | } | ||||
state->hashLastUnknownBlock.SetNull(); | state->hashLastUnknownBlock.SetNull(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/** Update tracking information about which blocks a peer is assumed to have. */ | /** Update tracking information about which blocks a peer is assumed to have. */ | ||||
static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) | static void UpdateBlockAvailability(NodeId nodeid, const BlockHash &hash) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
CNodeState *state = State(nodeid); | CNodeState *state = State(nodeid); | ||||
assert(state != nullptr); | assert(state != nullptr); | ||||
ProcessBlockAvailability(nodeid); | ProcessBlockAvailability(nodeid); | ||||
const CBlockIndex *pindex = LookupBlockIndex(hash); | const CBlockIndex *pindex = LookupBlockIndex(hash); | ||||
if (pindex && pindex->nChainWork > 0) { | if (pindex && pindex->nChainWork > 0) { | ||||
▲ Show 20 Lines • Show All 698 Lines • ▼ Show 20 Lines | void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, | ||||
bool fInitialDownload) { | bool fInitialDownload) { | ||||
const int nNewHeight = pindexNew->nHeight; | const int nNewHeight = pindexNew->nHeight; | ||||
connman->SetBestHeight(nNewHeight); | connman->SetBestHeight(nNewHeight); | ||||
SetServiceFlagsIBDCache(!fInitialDownload); | SetServiceFlagsIBDCache(!fInitialDownload); | ||||
if (!fInitialDownload) { | if (!fInitialDownload) { | ||||
// Find the hashes of all blocks that weren't previously in the best | // Find the hashes of all blocks that weren't previously in the best | ||||
// chain. | // chain. | ||||
std::vector<uint256> vHashes; | std::vector<BlockHash> vHashes; | ||||
const CBlockIndex *pindexToAnnounce = pindexNew; | const CBlockIndex *pindexToAnnounce = pindexNew; | ||||
while (pindexToAnnounce != pindexFork) { | while (pindexToAnnounce != pindexFork) { | ||||
vHashes.push_back(pindexToAnnounce->GetBlockHash()); | vHashes.push_back(pindexToAnnounce->GetBlockHash()); | ||||
pindexToAnnounce = pindexToAnnounce->pprev; | pindexToAnnounce = pindexToAnnounce->pprev; | ||||
if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) { | if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) { | ||||
// Limit announcements in case of a huge reorganization. Rely on | // Limit announcements in case of a huge reorganization. Rely on | ||||
// the peer's synchronization mechanism in that case. | // the peer's synchronization mechanism in that case. | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
// Relay inventory, but don't relay old inventory during initial block | // Relay inventory, but don't relay old inventory during initial block | ||||
// download. | // download. | ||||
connman->ForEachNode([nNewHeight, &vHashes](CNode *pnode) { | connman->ForEachNode([nNewHeight, &vHashes](CNode *pnode) { | ||||
if (nNewHeight > (pnode->nStartingHeight != -1 | if (nNewHeight > (pnode->nStartingHeight != -1 | ||||
? pnode->nStartingHeight - 2000 | ? pnode->nStartingHeight - 2000 | ||||
: 0)) { | : 0)) { | ||||
for (const uint256 &hash : reverse_iterate(vHashes)) { | for (const BlockHash &hash : reverse_iterate(vHashes)) { | ||||
pnode->PushBlockHash(hash); | pnode->PushBlockHash(hash); | ||||
} | } | ||||
} | } | ||||
}); | }); | ||||
connman->WakeMessageHandler(); | connman->WakeMessageHandler(); | ||||
} | } | ||||
nTimeBestReceived = GetTime(); | nTimeBestReceived = GetTime(); | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | switch (inv.type) { | ||||
// diminishing returns with 2 onward. | // diminishing returns with 2 onward. | ||||
const TxId txid(inv.hash); | const TxId txid(inv.hash); | ||||
return recentRejects->contains(inv.hash) || | return recentRejects->contains(inv.hash) || | ||||
g_mempool.exists(inv.hash) || | g_mempool.exists(inv.hash) || | ||||
pcoinsTip->HaveCoinInCache(COutPoint(txid, 0)) || | pcoinsTip->HaveCoinInCache(COutPoint(txid, 0)) || | ||||
pcoinsTip->HaveCoinInCache(COutPoint(txid, 1)); | pcoinsTip->HaveCoinInCache(COutPoint(txid, 1)); | ||||
} | } | ||||
case MSG_BLOCK: | case MSG_BLOCK: | ||||
return LookupBlockIndex(inv.hash) != nullptr; | return LookupBlockIndex(BlockHash(inv.hash)) != nullptr; | ||||
} | } | ||||
// Don't know what it is, just say we already got one | // Don't know what it is, just say we already got one | ||||
return true; | return true; | ||||
} | } | ||||
static void RelayTransaction(const CTransaction &tx, CConnman *connman) { | static void RelayTransaction(const CTransaction &tx, CConnman *connman) { | ||||
CInv inv(MSG_TX, tx.GetId()); | CInv inv(MSG_TX, tx.GetId()); | ||||
connman->ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); }); | connman->ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); }); | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static void ProcessGetBlockData(const Config &config, CNode *pfrom, | static void ProcessGetBlockData(const Config &config, CNode *pfrom, | ||||
const CInv &inv, CConnman *connman, | const CInv &inv, CConnman *connman, | ||||
const std::atomic<bool> &interruptMsgProc) { | const std::atomic<bool> &interruptMsgProc) { | ||||
const Consensus::Params &consensusParams = | const Consensus::Params &consensusParams = | ||||
config.GetChainParams().GetConsensus(); | config.GetChainParams().GetConsensus(); | ||||
const BlockHash hash(inv.hash); | |||||
bool send = false; | bool send = false; | ||||
std::shared_ptr<const CBlock> a_recent_block; | std::shared_ptr<const CBlock> a_recent_block; | ||||
std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block; | std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block; | ||||
{ | { | ||||
LOCK(cs_most_recent_block); | LOCK(cs_most_recent_block); | ||||
a_recent_block = most_recent_block; | a_recent_block = most_recent_block; | ||||
a_recent_compact_block = most_recent_compact_block; | a_recent_compact_block = most_recent_compact_block; | ||||
} | } | ||||
bool need_activate_chain = false; | bool need_activate_chain = false; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CBlockIndex *pindex = LookupBlockIndex(inv.hash); | const CBlockIndex *pindex = LookupBlockIndex(hash); | ||||
if (pindex) { | if (pindex) { | ||||
if (pindex->nChainTx && !pindex->IsValid(BlockValidity::SCRIPTS) && | if (pindex->nChainTx && !pindex->IsValid(BlockValidity::SCRIPTS) && | ||||
pindex->IsValid(BlockValidity::TREE)) { | pindex->IsValid(BlockValidity::TREE)) { | ||||
// If we have the block and all of its parents, but have not yet | // If we have the block and all of its parents, but have not yet | ||||
// validated it, we might be in the middle of connecting it (ie | // validated it, we might be in the middle of connecting it (ie | ||||
// in the unlock of cs_main before ActivateBestChain but after | // in the unlock of cs_main before ActivateBestChain but after | ||||
// AcceptBlock). In this case, we need to run ActivateBestChain | // AcceptBlock). In this case, we need to run ActivateBestChain | ||||
// prior to checking the relay conditions below. | // prior to checking the relay conditions below. | ||||
need_activate_chain = true; | need_activate_chain = true; | ||||
} | } | ||||
} | } | ||||
} // release cs_main before calling ActivateBestChain | } // release cs_main before calling ActivateBestChain | ||||
if (need_activate_chain) { | if (need_activate_chain) { | ||||
CValidationState state; | CValidationState state; | ||||
if (!ActivateBestChain(config, state, a_recent_block)) { | if (!ActivateBestChain(config, state, a_recent_block)) { | ||||
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", | LogPrint(BCLog::NET, "failed to activate chain (%s)\n", | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
} | } | ||||
} | } | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CBlockIndex *pindex = LookupBlockIndex(inv.hash); | const CBlockIndex *pindex = LookupBlockIndex(hash); | ||||
if (pindex) { | if (pindex) { | ||||
send = BlockRequestAllowed(pindex, consensusParams); | send = BlockRequestAllowed(pindex, consensusParams); | ||||
if (!send) { | if (!send) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"%s: ignoring request from peer=%i for old " | "%s: ignoring request from peer=%i for old " | ||||
"block that isn't in the main chain\n", | "block that isn't in the main chain\n", | ||||
__func__, pfrom->GetId()); | __func__, pfrom->GetId()); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 101 Lines • ▼ Show 20 Lines | if (send && pindex->nStatus.hasData()) { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, | pfrom, | ||||
msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); | msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); | ||||
} | } | ||||
} | } | ||||
// Trigger the peer node to send a getblocks request for the next batch | // Trigger the peer node to send a getblocks request for the next batch | ||||
// of inventory. | // of inventory. | ||||
if (inv.hash == pfrom->hashContinue) { | if (hash == pfrom->hashContinue) { | ||||
// Bypass PushInventory, this must send even if redundant, and we | // Bypass PushInventory, this must send even if redundant, and we | ||||
// want it right after the last block so they don't wait for other | // want it right after the last block so they don't wait for other | ||||
// stuff first. | // stuff first. | ||||
std::vector<CInv> vInv; | std::vector<CInv> vInv; | ||||
vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); | vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); | ||||
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv)); | connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv)); | ||||
pfrom->hashContinue.SetNull(); | pfrom->hashContinue = BlockHash(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static void ProcessGetData(const Config &config, CNode *pfrom, | static void ProcessGetData(const Config &config, CNode *pfrom, | ||||
CConnman *connman, | CConnman *connman, | ||||
const std::atomic<bool> &interruptMsgProc) | const std::atomic<bool> &interruptMsgProc) | ||||
LOCKS_EXCLUDED(cs_main) { | LOCKS_EXCLUDED(cs_main) { | ||||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | const CBlockIndex *pindexLast = nullptr; | ||||
if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == | if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == | ||||
0) { | 0) { | ||||
// The peer is sending us many headers we can't connect. | // The peer is sending us many headers we can't connect. | ||||
Misbehaving(pfrom, 20, "too-many-unconnected-headers"); | Misbehaving(pfrom, 20, "too-many-unconnected-headers"); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
uint256 hashLastBlock; | BlockHash hashLastBlock; | ||||
for (const CBlockHeader &header : headers) { | for (const CBlockHeader &header : headers) { | ||||
if (!hashLastBlock.IsNull() && | if (!hashLastBlock.IsNull() && | ||||
header.hashPrevBlock != hashLastBlock) { | header.hashPrevBlock != hashLastBlock) { | ||||
Misbehaving(pfrom, 20, "disconnected-header"); | Misbehaving(pfrom, 20, "disconnected-header"); | ||||
return error("non-continuous headers sequence"); | return error("non-continuous headers sequence"); | ||||
} | } | ||||
hashLastBlock = header.GetHash(); | hashLastBlock = header.GetHash(); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 633 Lines • ▼ Show 20 Lines | if (strCommand == NetMsgType::INV) { | ||||
return true; | return true; | ||||
} | } | ||||
bool fAlreadyHave = AlreadyHave(inv); | bool fAlreadyHave = AlreadyHave(inv); | ||||
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), | LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), | ||||
fAlreadyHave ? "have" : "new", pfrom->GetId()); | fAlreadyHave ? "have" : "new", pfrom->GetId()); | ||||
if (inv.type == MSG_BLOCK) { | if (inv.type == MSG_BLOCK) { | ||||
UpdateBlockAvailability(pfrom->GetId(), inv.hash); | const BlockHash hash(inv.hash); | ||||
UpdateBlockAvailability(pfrom->GetId(), hash); | |||||
if (!fAlreadyHave && !fImporting && !fReindex && | if (!fAlreadyHave && !fImporting && !fReindex && | ||||
!mapBlocksInFlight.count(inv.hash)) { | !mapBlocksInFlight.count(hash)) { | ||||
// We used to request the full block here, but since | // We used to request the full block here, but since | ||||
// headers-announcements are now the primary method of | // headers-announcements are now the primary method of | ||||
// announcement on the network, and since, in the case that | // announcement on the network, and since, in the case that | ||||
// a node fell back to inv we probably have a reorg which we | // a node fell back to inv we probably have a reorg which we | ||||
// should get the headers for first, we now only provide a | // should get the headers for first, we now only provide a | ||||
// getheaders response here. When we receive the headers, we | // getheaders response here. When we receive the headers, we | ||||
// will then ask for the blocks we need. | // will then ask for the blocks we need. | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, | pfrom, | ||||
msgMaker.Make(NetMsgType::GETHEADERS, | msgMaker.Make(NetMsgType::GETHEADERS, | ||||
chainActive.GetLocator(pindexBestHeader), | chainActive.GetLocator(pindexBestHeader), | ||||
inv.hash)); | hash)); | ||||
LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", | LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", | ||||
pindexBestHeader->nHeight, inv.hash.ToString(), | pindexBestHeader->nHeight, hash.ToString(), | ||||
pfrom->GetId()); | pfrom->GetId()); | ||||
} | } | ||||
} else { | } else { | ||||
pfrom->AddInventoryKnown(inv); | pfrom->AddInventoryKnown(inv); | ||||
if (fBlocksOnly) { | if (fBlocksOnly) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"transaction (%s) inv sent in violation of " | "transaction (%s) inv sent in violation of " | ||||
"protocol peer=%d\n", | "protocol peer=%d\n", | ||||
▲ Show 20 Lines • Show All 165 Lines • ▼ Show 20 Lines | if (strCommand == NetMsgType::GETBLOCKTXN) { | ||||
assert(ret); | assert(ret); | ||||
SendBlockTransactions(block, req, pfrom, connman); | SendBlockTransactions(block, req, pfrom, connman); | ||||
return true; | return true; | ||||
} | } | ||||
if (strCommand == NetMsgType::GETHEADERS) { | if (strCommand == NetMsgType::GETHEADERS) { | ||||
CBlockLocator locator; | CBlockLocator locator; | ||||
uint256 hashStop; | BlockHash hashStop; | ||||
vRecv >> locator >> hashStop; | vRecv >> locator >> hashStop; | ||||
if (locator.vHave.size() > MAX_LOCATOR_SZ) { | if (locator.vHave.size() > MAX_LOCATOR_SZ) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"getheaders locator size %lld > %d, disconnect peer=%d\n", | "getheaders locator size %lld > %d, disconnect peer=%d\n", | ||||
locator.vHave.size(), MAX_LOCATOR_SZ, pfrom->GetId()); | locator.vHave.size(), MAX_LOCATOR_SZ, pfrom->GetId()); | ||||
pfrom->fDisconnect = true; | pfrom->fDisconnect = true; | ||||
return true; | return true; | ||||
▲ Show 20 Lines • Show All 1,537 Lines • ▼ Show 20 Lines | // | ||||
// ensure pindexBestKnownBlock is up-to-date | // ensure pindexBestKnownBlock is up-to-date | ||||
ProcessBlockAvailability(pto->GetId()); | ProcessBlockAvailability(pto->GetId()); | ||||
if (!fRevertToInv) { | if (!fRevertToInv) { | ||||
bool fFoundStartingHeader = false; | bool fFoundStartingHeader = false; | ||||
// Try to find first header that our peer doesn't have, and then | // Try to find first header that our peer doesn't have, and then | ||||
// send all headers past that one. If we come across an headers that | // send all headers past that one. If we come across an headers that | ||||
// aren't on chainActive, give up. | // aren't on chainActive, give up. | ||||
for (const uint256 &hash : pto->vBlockHashesToAnnounce) { | for (const BlockHash &hash : pto->vBlockHashesToAnnounce) { | ||||
const CBlockIndex *pindex = LookupBlockIndex(hash); | const CBlockIndex *pindex = LookupBlockIndex(hash); | ||||
assert(pindex); | assert(pindex); | ||||
if (chainActive[pindex->nHeight] != pindex) { | if (chainActive[pindex->nHeight] != pindex) { | ||||
// Bail out if we reorged away from this block | // Bail out if we reorged away from this block | ||||
fRevertToInv = true; | fRevertToInv = true; | ||||
break; | break; | ||||
} | } | ||||
if (pBestIndex != nullptr && pindex->pprev != pBestIndex) { | if (pBestIndex != nullptr && pindex->pprev != pBestIndex) { | ||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | // | ||||
fRevertToInv = true; | fRevertToInv = true; | ||||
} | } | ||||
} | } | ||||
if (fRevertToInv) { | if (fRevertToInv) { | ||||
// If falling back to using an inv, just try to inv the tip. The | // If falling back to using an inv, just try to inv the tip. The | ||||
// last entry in vBlockHashesToAnnounce was our tip at some point in | // last entry in vBlockHashesToAnnounce was our tip at some point in | ||||
// the past. | // the past. | ||||
if (!pto->vBlockHashesToAnnounce.empty()) { | if (!pto->vBlockHashesToAnnounce.empty()) { | ||||
const uint256 &hashToAnnounce = | const BlockHash &hashToAnnounce = | ||||
pto->vBlockHashesToAnnounce.back(); | pto->vBlockHashesToAnnounce.back(); | ||||
const CBlockIndex *pindex = LookupBlockIndex(hashToAnnounce); | const CBlockIndex *pindex = LookupBlockIndex(hashToAnnounce); | ||||
assert(pindex); | assert(pindex); | ||||
// Warn if we're announcing a block that is not on the main | // Warn if we're announcing a block that is not on the main | ||||
// chain. This should be very rare and could be optimized out. | // chain. This should be very rare and could be optimized out. | ||||
// Just log for now. | // Just log for now. | ||||
if (chainActive[pindex->nHeight] != pindex) { | if (chainActive[pindex->nHeight] != pindex) { | ||||
▲ Show 20 Lines • Show All 414 Lines • Show Last 20 Lines |