Skip to content

Commit d04aeba

Browse files
committed
Merge #9014: Fix block-connection performance regression
dd0df81 Document ConnectBlock connectTrace postconditions (Matt Corallo) 2d6e561 Switch pblock in ProcessNewBlock to a shared_ptr (Matt Corallo) 2736c44 Make the optional pblock in ActivateBestChain a shared_ptr (Matt Corallo) ae4db44 Create a shared_ptr for the block we're connecting in ActivateBCS (Matt Corallo) fd9d890 Keep blocks as shared_ptrs, instead of copying txn in ConnectTip (Matt Corallo) 6fdd43b Add struct to track block-connect-time-generated info for callbacks (Matt Corallo)
2 parents 46904ee + dd0df81 commit d04aeba

File tree

7 files changed

+60
-40
lines changed

7 files changed

+60
-40
lines changed

src/net_processing.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,7 +1894,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
18941894
BlockTransactions resp;
18951895
vRecv >> resp;
18961896

1897-
CBlock block;
1897+
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
18981898
bool fBlockRead = false;
18991899
{
19001900
LOCK(cs_main);
@@ -1907,7 +1907,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
19071907
}
19081908

19091909
PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
1910-
ReadStatus status = partialBlock.FillBlock(block, resp.txn);
1910+
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
19111911
if (status == READ_STATUS_INVALID) {
19121912
MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist
19131913
Misbehaving(pfrom->GetId(), 100);
@@ -1950,7 +1950,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
19501950
bool fNewBlock = false;
19511951
// Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
19521952
// even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
1953-
ProcessNewBlock(chainparams, &block, true, NULL, &fNewBlock);
1953+
ProcessNewBlock(chainparams, pblock, true, NULL, &fNewBlock);
19541954
if (fNewBlock)
19551955
pfrom->nLastBlockTime = GetTime();
19561956
}
@@ -2111,17 +2111,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
21112111

21122112
else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
21132113
{
2114-
CBlock block;
2115-
vRecv >> block;
2114+
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
2115+
vRecv >> *pblock;
21162116

2117-
LogPrint("net", "received block %s peer=%d\n", block.GetHash().ToString(), pfrom->id);
2117+
LogPrint("net", "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->id);
21182118

21192119
// Process all blocks from whitelisted peers, even if not requested,
21202120
// unless we're still syncing with the network.
21212121
// Such an unrequested block may still be processed, subject to the
21222122
// conditions in AcceptBlock().
21232123
bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
2124-
const uint256 hash(block.GetHash());
2124+
const uint256 hash(pblock->GetHash());
21252125
{
21262126
LOCK(cs_main);
21272127
// Also always process if we requested the block explicitly, as we may
@@ -2132,7 +2132,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
21322132
mapBlockSource.emplace(hash, std::make_pair(pfrom->GetId(), true));
21332133
}
21342134
bool fNewBlock = false;
2135-
ProcessNewBlock(chainparams, &block, forceProcessing, NULL, &fNewBlock);
2135+
ProcessNewBlock(chainparams, pblock, forceProcessing, NULL, &fNewBlock);
21362136
if (fNewBlock)
21372137
pfrom->nLastBlockTime = GetTime();
21382138
}

src/rpc/blockchain.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,7 +1319,7 @@ UniValue invalidateblock(const JSONRPCRequest& request)
13191319
}
13201320

13211321
if (state.IsValid()) {
1322-
ActivateBestChain(state, Params(), NULL);
1322+
ActivateBestChain(state, Params());
13231323
}
13241324

13251325
if (!state.IsValid()) {
@@ -1357,7 +1357,7 @@ UniValue reconsiderblock(const JSONRPCRequest& request)
13571357
}
13581358

13591359
CValidationState state;
1360-
ActivateBestChain(state, Params(), NULL);
1360+
ActivateBestChain(state, Params());
13611361

13621362
if (!state.IsValid()) {
13631363
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());

src/rpc/mining.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG
131131
if (pblock->nNonce == nInnerLoopCount) {
132132
continue;
133133
}
134-
if (!ProcessNewBlock(Params(), pblock, true, NULL, NULL))
134+
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
135+
if (!ProcessNewBlock(Params(), shared_pblock, true, NULL, NULL))
135136
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
136137
++nHeight;
137138
blockHashes.push_back(pblock->GetHash().GetHex());
@@ -728,7 +729,8 @@ UniValue submitblock(const JSONRPCRequest& request)
728729
+ HelpExampleRpc("submitblock", "\"mydata\"")
729730
);
730731

731-
CBlock block;
732+
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
733+
CBlock& block = *blockptr;
732734
if (!DecodeHexBlk(block, request.params[0].get_str()))
733735
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
734736

@@ -758,7 +760,7 @@ UniValue submitblock(const JSONRPCRequest& request)
758760

759761
submitblock_StateCatcher sc(block.GetHash());
760762
RegisterValidationInterface(&sc);
761-
bool fAccepted = ProcessNewBlock(Params(), &block, true, NULL, NULL);
763+
bool fAccepted = ProcessNewBlock(Params(), blockptr, true, NULL, NULL);
762764
UnregisterValidationInterface(&sc);
763765
if (fBlockPresent)
764766
{

src/test/miner_tests.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
223223
txFirst.push_back(pblock->vtx[0]);
224224
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
225225
pblock->nNonce = blockinfo[i].nonce;
226-
BOOST_CHECK(ProcessNewBlock(chainparams, pblock, true, NULL, NULL));
226+
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
227+
BOOST_CHECK(ProcessNewBlock(chainparams, shared_pblock, true, NULL, NULL));
227228
pblock->hashPrevBlock = pblock->GetHash();
228229
}
229230

src/test/test_bitcoin.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
127127

128128
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
129129

130-
ProcessNewBlock(chainparams, &block, true, NULL, NULL);
130+
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
131+
ProcessNewBlock(chainparams, shared_pblock, true, NULL, NULL);
131132

132133
CBlock result = block;
133134
return result;

src/validation.cpp

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2152,29 +2152,45 @@ static int64_t nTimeFlush = 0;
21522152
static int64_t nTimeChainState = 0;
21532153
static int64_t nTimePostConnect = 0;
21542154

2155+
/**
2156+
* Used to track conflicted transactions removed from mempool and transactions
2157+
* applied to the UTXO state as a part of a single ActivateBestChainStep call.
2158+
*/
2159+
struct ConnectTrace {
2160+
std::vector<CTransactionRef> txConflicted;
2161+
std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected;
2162+
};
2163+
21552164
/**
21562165
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
21572166
* corresponding to pindexNew, to bypass loading it again from disk.
2167+
*
2168+
* The block is always added to connectTrace (either after loading from disk or by copying
2169+
* pblock) - if that is not intended, care must be taken to remove the last entry in
2170+
* blocksConnected in case of failure.
21582171
*/
2159-
bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock, std::vector<CTransactionRef> &txConflicted, std::vector<std::tuple<CTransactionRef,CBlockIndex*,int>> &txChanged)
2172+
bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace)
21602173
{
21612174
assert(pindexNew->pprev == chainActive.Tip());
21622175
// Read block from disk.
21632176
int64_t nTime1 = GetTimeMicros();
2164-
CBlock block;
21652177
if (!pblock) {
2166-
if (!ReadBlockFromDisk(block, pindexNew, chainparams.GetConsensus()))
2178+
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
2179+
connectTrace.blocksConnected.emplace_back(pindexNew, pblockNew);
2180+
if (!ReadBlockFromDisk(*pblockNew, pindexNew, chainparams.GetConsensus()))
21672181
return AbortNode(state, "Failed to read block");
2168-
pblock = &block;
2182+
} else {
2183+
connectTrace.blocksConnected.emplace_back(pindexNew, pblock);
21692184
}
2185+
const CBlock& blockConnecting = *connectTrace.blocksConnected.back().second;
21702186
// Apply the block atomically to the chain state.
21712187
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
21722188
int64_t nTime3;
21732189
LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001);
21742190
{
21752191
CCoinsViewCache view(pcoinsTip);
2176-
bool rv = ConnectBlock(*pblock, state, pindexNew, view, chainparams);
2177-
GetMainSignals().BlockChecked(*pblock, state);
2192+
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams);
2193+
GetMainSignals().BlockChecked(blockConnecting, state);
21782194
if (!rv) {
21792195
if (state.IsInvalid())
21802196
InvalidBlockFound(pindexNew, state);
@@ -2192,13 +2208,10 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
21922208
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
21932209
LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
21942210
// Remove conflicting transactions from the mempool.;
2195-
mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, &txConflicted, !IsInitialBlockDownload());
2211+
mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight, &connectTrace.txConflicted, !IsInitialBlockDownload());
21962212
// Update chainActive & related variables.
21972213
UpdateTip(pindexNew, chainparams);
21982214

2199-
for (unsigned int i=0; i < pblock->vtx.size(); i++)
2200-
txChanged.emplace_back(pblock->vtx[i], pindexNew, i);
2201-
22022215
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
22032216
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
22042217
LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
@@ -2279,7 +2292,7 @@ static void PruneBlockIndexCandidates() {
22792292
* Try to make some progress towards making pindexMostWork the active block.
22802293
* pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork.
22812294
*/
2282-
static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound, std::vector<CTransactionRef>& txConflicted, std::vector<std::tuple<CTransactionRef,CBlockIndex*,int>>& txChanged)
2295+
static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
22832296
{
22842297
AssertLockHeld(cs_main);
22852298
const CBlockIndex *pindexOldTip = chainActive.Tip();
@@ -2312,14 +2325,16 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
23122325

23132326
// Connect new blocks.
23142327
BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
2315-
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL, txConflicted, txChanged)) {
2328+
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace)) {
23162329
if (state.IsInvalid()) {
23172330
// The block violates a consensus rule.
23182331
if (!state.CorruptionPossible())
23192332
InvalidChainFound(vpindexToConnect.back());
23202333
state = CValidationState();
23212334
fInvalidFound = true;
23222335
fContinue = false;
2336+
// If we didn't actually connect the block, don't notify listeners about it
2337+
connectTrace.blocksConnected.pop_back();
23232338
break;
23242339
} else {
23252340
// A system error occurred (disk space, database error, ...).
@@ -2377,20 +2392,16 @@ static void NotifyHeaderTip() {
23772392
* or an activated best chain. pblock is either NULL or a pointer to a block
23782393
* that is already loaded (to avoid loading it again from disk).
23792394
*/
2380-
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) {
2395+
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
23812396
CBlockIndex *pindexMostWork = NULL;
23822397
CBlockIndex *pindexNewTip = NULL;
2383-
std::vector<std::tuple<CTransactionRef,CBlockIndex*,int>> txChanged;
2384-
if (pblock)
2385-
txChanged.reserve(pblock->vtx.size());
23862398
do {
2387-
txChanged.clear();
23882399
boost::this_thread::interruption_point();
23892400
if (ShutdownRequested())
23902401
break;
23912402

23922403
const CBlockIndex *pindexFork;
2393-
std::vector<CTransactionRef> txConflicted;
2404+
ConnectTrace connectTrace;
23942405
bool fInitialDownload;
23952406
{
23962407
LOCK(cs_main);
@@ -2404,7 +2415,8 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
24042415
return true;
24052416

24062417
bool fInvalidFound = false;
2407-
if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fInvalidFound, txConflicted, txChanged))
2418+
std::shared_ptr<const CBlock> nullBlockPtr;
2419+
if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
24082420
return false;
24092421

24102422
if (fInvalidFound) {
@@ -2421,13 +2433,17 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
24212433

24222434
// throw all transactions though the signal-interface
24232435
// while _not_ holding the cs_main lock
2424-
for (const auto& tx : txConflicted)
2436+
for (const auto& tx : connectTrace.txConflicted)
24252437
{
24262438
GetMainSignals().SyncTransaction(*tx, pindexNewTip, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
24272439
}
24282440
// ... and about transactions that got confirmed:
2429-
for (unsigned int i = 0; i < txChanged.size(); i++)
2430-
GetMainSignals().SyncTransaction(*std::get<0>(txChanged[i]), std::get<1>(txChanged[i]), std::get<2>(txChanged[i]));
2441+
for (const auto& pair : connectTrace.blocksConnected) {
2442+
assert(pair.second);
2443+
const CBlock& block = *(pair.second);
2444+
for (unsigned int i = 0; i < block.vtx.size(); i++)
2445+
GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i);
2446+
}
24312447

24322448
// Notify external listeners about the new tip.
24332449
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
@@ -3112,7 +3128,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha
31123128
return true;
31133129
}
31143130

3115-
bool ProcessNewBlock(const CChainParams& chainparams, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool *fNewBlock)
3131+
bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool *fNewBlock)
31163132
{
31173133
{
31183134
LOCK(cs_main);

src/validation.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
233233
* @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call
234234
* @return True if state.IsValid()
235235
*/
236-
bool ProcessNewBlock(const CChainParams& chainparams, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool* fNewBlock);
236+
bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool* fNewBlock);
237237

238238
/**
239239
* Process incoming block headers.
@@ -278,7 +278,7 @@ std::string GetWarnings(const std::string& strFor);
278278
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
279279
bool GetTransaction(const uint256 &hash, CTransactionRef &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false);
280280
/** Find the best known block, and make it the tip of the block chain */
281-
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL);
281+
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>());
282282
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
283283

284284
/**

0 commit comments

Comments
 (0)