Skip to content

Commit

Permalink
Backport Bitcoin PR#8865: Decouple peer-processing-logic from block-c…
Browse files Browse the repository at this point in the history
…onnection-logic (#1556)

* Make validationinterface.UpdatedBlockTip more verbose

In anticipation of making all the callbacks out of block processing
flow through it. Note that vHashes will always have something in it
since pindexFork != pindexNewTip.

* Remove duplicate nBlocksEstimate cmp (we already checked IsIBD())

* Remove CConnman parameter from ProcessNewBlock/ActivateBestChain

* Remove SyncWithWallets wrapper function

* Move net-processing logic definitions together in main.h

* Use CValidationInterface from chain logic to notify peer logic

This adds a new CValidationInterface subclass, defined in main.h,
to receive notifications of UpdatedBlockTip and use that to push
blocks to peers, instead of doing it directly from
ActivateBestChain.

* Always call UpdatedBlockTip, even if blocks were only disconnected

* Use BlockChecked signal to send reject messages from mapBlockSource
  • Loading branch information
OlegGirko authored and UdjinM6 committed Jul 28, 2017
1 parent cc4db34 commit a3c8cb2
Show file tree
Hide file tree
Showing 12 changed files with 133 additions and 106 deletions.
5 changes: 5 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ static const bool DEFAULT_DISABLE_SAFEMODE = false;
static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;

std::unique_ptr<CConnman> g_connman;
std::unique_ptr<PeerLogicValidation> peerLogic;

#if ENABLE_ZMQ
static CZMQNotificationInterface* pzmqNotificationInterface = NULL;
Expand Down Expand Up @@ -228,6 +229,8 @@ void PrepareShutdown()
#endif
GenerateBitcoins(false, 0, Params(), *g_connman);
MapPort(false);
UnregisterValidationInterface(peerLogic.get());
peerLogic.reset();
g_connman.reset();

// STORE DATA CACHES INTO SERIALIZED DAT FILES
Expand Down Expand Up @@ -1316,6 +1319,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
g_connman = std::unique_ptr<CConnman>(new CConnman());
CConnman& connman = *g_connman;

peerLogic.reset(new PeerLogicValidation(&connman));
RegisterValidationInterface(peerLogic.get());
RegisterNodeSignals(GetNodeSignals());

// sanitize comments per BIP-0014, format user agent and check total size
Expand Down
121 changes: 65 additions & 56 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1540,7 +1540,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
}
}

if(!fDryRun) SyncWithWallets(tx, NULL);
if(!fDryRun)
GetMainSignals().SyncTransaction(tx, NULL);

return true;
}
Expand Down Expand Up @@ -1966,17 +1967,6 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
}

void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
int nDoS = 0;
if (state.IsInvalid(nDoS)) {
std::map<uint256, NodeId>::iterator it = mapBlockSource.find(pindex->GetBlockHash());
if (it != mapBlockSource.end() && State(it->second)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()};
State(it->second)->rejects.push_back(reject);
if (nDoS > 0)
Misbehaving(it->second, nDoS);
}
}
if (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
Expand Down Expand Up @@ -3047,7 +3037,7 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
SyncWithWallets(tx, NULL);
GetMainSignals().SyncTransaction(tx, NULL);
}
return true;
}
Expand Down Expand Up @@ -3086,7 +3076,6 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
InvalidBlockFound(pindexNew, state);
return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString());
}
mapBlockSource.erase(pindexNew->GetBlockHash());
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001);
assert(view.Flush());
Expand All @@ -3106,11 +3095,11 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
// Tell wallet about transactions that went from mempool
// to conflicted:
BOOST_FOREACH(const CTransaction &tx, txConflicted) {
SyncWithWallets(tx, NULL);
GetMainSignals().SyncTransaction(tx, NULL);
}
// ... and about transactions that got confirmed:
BOOST_FOREACH(const CTransaction &tx, pblock->vtx) {
SyncWithWallets(tx, pblock);
GetMainSignals().SyncTransaction(tx, pblock);
}

int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
Expand Down Expand Up @@ -3336,7 +3325,7 @@ static void NotifyHeaderTip() {
* or an activated best chain. pblock is either NULL or a pointer to a block
* that is already loaded (to avoid loading it again from disk).
*/
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock, CConnman* connman) {
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) {
CBlockIndex *pindexMostWork = NULL;
CBlockIndex *pindexNewTip = NULL;
do {
Expand All @@ -3346,7 +3335,6 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,

const CBlockIndex *pindexFork;
bool fInitialDownload;
int nNewHeight;
{
LOCK(cs_main);
CBlockIndex *pindexOldTip = chainActive.Tip();
Expand All @@ -3369,49 +3357,17 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
pindexNewTip = chainActive.Tip();
pindexFork = chainActive.FindFork(pindexOldTip);
fInitialDownload = IsInitialBlockDownload();
nNewHeight = chainActive.Height();
}
// When we reach this point, we switched to a new tip (stored in pindexNewTip).

// Notifications/callbacks that can run without cs_main
if(connman)
connman->SetBestHeight(chainActive.Height());

// Notify external listeners about the new tip.
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);

// Always notify the UI if a new block tip was connected
if (pindexFork != pindexNewTip) {
uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);

if (!fInitialDownload) {
// Find the hashes of all blocks that weren't previously in the best chain.
std::vector<uint256> vHashes;
CBlockIndex *pindexToAnnounce = pindexNewTip;
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.
int nBlockEstimate = 0;
if (fCheckpointsEnabled)
nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints());
if(connman) {
connman->ForEachNode([nNewHeight, nBlockEstimate, &vHashes](CNode* pnode) {
if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) {
BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) {
pnode->PushBlockHash(hash);
}
}
});
}
// Notify external listeners about the new tip.
if (!vHashes.empty()) {
GetMainSignals().UpdatedBlockTip(pindexNewTip);
}
}
}
} while (pindexNewTip != pindexMostWork);
CheckBlockIndex(chainparams.GetConsensus());
Expand Down Expand Up @@ -4003,7 +3959,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned
}


bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, CConnman* connman)
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp)
{
{
LOCK(cs_main);
Expand All @@ -4025,7 +3981,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, C

NotifyHeaderTip();

if (!ActivateBestChain(state, chainparams, pblock, connman))
if (!ActivateBestChain(state, chainparams, pblock))
return error("%s: ActivateBestChain failed", __func__);

masternodeSync.IsBlockchainSynced(true);
Expand Down Expand Up @@ -4909,6 +4865,59 @@ std::string GetWarnings(const std::string& strFor)



//////////////////////////////////////////////////////////////////////////////
//
// blockchain -> download logic notification
//

void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
const int nNewHeight = pindexNew->nHeight;
connman->SetBestHeight(nNewHeight);

if (!fInitialDownload) {
// Find the hashes of all blocks that weren't previously in the best chain.
std::vector<uint256> 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)) {
BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) {
pnode->PushBlockHash(hash);
}
}
});
}
}

void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) {
LOCK(cs_main);

const uint256 hash(block.GetHash());
std::map<uint256, NodeId>::iterator it = mapBlockSource.find(hash);

int nDoS = 0;
if (state.IsInvalid(nDoS)) {
if (it != mapBlockSource.end() && State(it->second)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
State(it->second)->rejects.push_back(reject);
if (nDoS > 0)
Misbehaving(it->second, nDoS);
}
}
if (it != mapBlockSource.end())
mapBlockSource.erase(it);
}

//////////////////////////////////////////////////////////////////////////////
//
// Messages
Expand Down Expand Up @@ -6107,7 +6116,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Such an unrequested block may still be processed, subject to the
// conditions in AcceptBlock().
bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL, &connman);
ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL);
int nDoS;
if (state.IsInvalid(nDoS)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
Expand Down
72 changes: 44 additions & 28 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "net.h"
#include "script/script_error.h"
#include "sync.h"
#include "validationinterface.h"
#include "versionbits.h"
#include "spentindex.h"

Expand All @@ -42,7 +43,6 @@ class CTxMemPool;
class CValidationInterface;
class CValidationState;

struct CNodeStateStats;
struct LockPoints;

/** Default for accepting alerts from the P2P network. */
Expand Down Expand Up @@ -189,11 +189,6 @@ static const unsigned int DEFAULT_CHECKLEVEL = 3;
// Setting the target to > than 550MB will make it likely we can respect the target.
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;

/** Register with a network node to receive its signals */
void RegisterNodeSignals(CNodeSignals& nodeSignals);
/** Unregister a network node */
void UnregisterNodeSignals(CNodeSignals& nodeSignals);

/**
* Process an incoming block. This only returns after the best known valid
* block is made active. Note that it does not, however, guarantee that the
Expand All @@ -206,7 +201,7 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals);
* @param[out] dbp The already known disk position of pblock, or NULL if not yet stored.
* @return True if state.IsValid()
*/
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, CConnman* connman);
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp);
/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
/** Open a block file (blk?????.dat) */
Expand All @@ -223,15 +218,6 @@ bool InitBlockIndex(const CChainParams& chainparams);
bool LoadBlockIndex();
/** Unload database information */
void UnloadBlockIndex();
/** Process protocol messages received from a given node */
bool ProcessMessages(CNode* pfrom, CConnman& connman);
/**
* Send queued protocol messages to be sent to a give node.
*
* @param[in] pto The node which we are sending messages to.
* @param[in] connman The connection manager for that node.
*/
bool SendMessages(CNode* pto, CConnman& connman);
/** Run an instance of the script checking thread */
void ThreadScriptCheck();
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
Expand All @@ -247,7 +233,7 @@ std::string GetWarnings(const std::string& strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false);
/** Find the best known block, and make it the tip of the block chain */
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL, CConnman* connman = NULL);
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL);

double ConvertBitsToDouble(unsigned int nBits);
CAmount GetBlockSubsidy(int nBits, int nHeight, const Consensus::Params& consensusParams, bool fSuperblockPartOnly = false);
Expand Down Expand Up @@ -277,10 +263,6 @@ void UnlinkPrunedFiles(std::set<int>& setFilesToPrune);

/** Create a new block index entry for a given block hash */
CBlockIndex * InsertBlockIndex(uint256 hash);
/** Get statistics from node state */
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
/** Increase a node's misbehavior score. */
void Misbehaving(NodeId nodeid, int howmuch);
/** Flush all state, indexes and buffers to disk. */
void FlushStateToDisk();
/** Prune block files and flush state to disk. */
Expand All @@ -301,13 +283,6 @@ std::string FormatStateMessage(const CValidationState &state);
/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);

struct CNodeStateStats {
int nMisbehavior;
int nSyncHeight;
int nCommonHeight;
std::vector<int> vHeightInFlight;
};

struct CTimestampIndexIteratorKey {
unsigned int timestamp;

Expand Down Expand Up @@ -863,4 +838,45 @@ static const unsigned int REJECT_ALREADY_KNOWN = 0x101;
/** Transaction conflicts with a transaction already known */
static const unsigned int REJECT_CONFLICT = 0x102;

// The following things handle network-processing logic
// (and should be moved to a separate file)

/** Register with a network node to receive its signals */
void RegisterNodeSignals(CNodeSignals& nodeSignals);
/** Unregister a network node */
void UnregisterNodeSignals(CNodeSignals& nodeSignals);

class PeerLogicValidation : public CValidationInterface {
private:
CConnman* connman;

public:
PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) {}

virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
virtual void BlockChecked(const CBlock& block, const CValidationState& state);
};

struct CNodeStateStats {
int nMisbehavior;
int nSyncHeight;
int nCommonHeight;
std::vector<int> vHeightInFlight;
};

/** Get statistics from node state */
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
/** Increase a node's misbehavior score. */
void Misbehaving(NodeId nodeid, int howmuch);

/** Process protocol messages received from a given node */
bool ProcessMessages(CNode* pfrom, CConnman& connman);
/**
* Send queued protocol messages to be sent to a give node.
*
* @param[in] pto The node which we are sending messages to.
* @param[in] connman The connection manager for that node.
*/
bool SendMessages(CNode* pto, CConnman& connman);

#endif // BITCOIN_MAIN_H
Loading

0 comments on commit a3c8cb2

Please sign in to comment.