mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-18 22:35:39 +01:00
Batch block connection during IBD
During the initial block download (or -loadblock), delay connection of new blocks a bit, and perform them in a single action. This reduces the load on the database engine, as subsequent blocks often update an earlier block's transaction already.
This commit is contained in:
150
src/main.cpp
150
src/main.cpp
@@ -168,6 +168,7 @@ bool CCoinsView::SetCoins(uint256 txid, const CCoins &coins) { return false; }
|
||||
bool CCoinsView::HaveCoins(uint256 txid) { return false; }
|
||||
CBlockIndex *CCoinsView::GetBestBlock() { return NULL; }
|
||||
bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; }
|
||||
bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return false; }
|
||||
|
||||
CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { }
|
||||
bool CCoinsViewBacked::GetCoins(uint256 txid, CCoins &coins) { return base->GetCoins(txid, coins); }
|
||||
@@ -176,13 +177,7 @@ bool CCoinsViewBacked::HaveCoins(uint256 txid) { return base->HaveCoins(txid); }
|
||||
CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); }
|
||||
bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); }
|
||||
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
||||
|
||||
CCoinsViewDB::CCoinsViewDB(CCoinsDB &dbIn) : db(dbIn) {}
|
||||
bool CCoinsViewDB::GetCoins(uint256 txid, CCoins &coins) { return db.ReadCoins(txid, coins); }
|
||||
bool CCoinsViewDB::SetCoins(uint256 txid, const CCoins &coins) { return db.WriteCoins(txid, coins); }
|
||||
bool CCoinsViewDB::HaveCoins(uint256 txid) { return db.HaveCoins(txid); }
|
||||
CBlockIndex *CCoinsViewDB::GetBestBlock() { return pindexBest; }
|
||||
bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { return db.WriteHashBestChain(pindex->GetBlockHash()); }
|
||||
bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); }
|
||||
|
||||
CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { }
|
||||
|
||||
@@ -218,18 +213,24 @@ bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::Flush() {
|
||||
for (std::map<uint256,CCoins>::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
|
||||
if (!base->SetCoins(it->first, it->second))
|
||||
return false;
|
||||
}
|
||||
if (!base->SetBestBlock(pindexTip))
|
||||
return false;
|
||||
cacheCoins.clear();
|
||||
pindexTip = NULL;
|
||||
bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) {
|
||||
for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++)
|
||||
cacheCoins[it->first] = it->second;
|
||||
pindexTip = pindex;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::Flush() {
|
||||
bool fOk = base->BatchWrite(cacheCoins, pindexTip);
|
||||
if (fOk)
|
||||
cacheCoins.clear();
|
||||
return fOk;
|
||||
}
|
||||
|
||||
unsigned int CCoinsViewCache::GetCacheSize() {
|
||||
return cacheCoins.size();
|
||||
}
|
||||
|
||||
/** CCoinsView that brings transactions from a memorypool into view.
|
||||
It does not check for spendings by memory pool transactions. */
|
||||
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
|
||||
@@ -249,7 +250,7 @@ bool CCoinsViewMemPool::HaveCoins(uint256 txid) {
|
||||
return mempool.exists(txid) || base->HaveCoins(txid);
|
||||
}
|
||||
|
||||
|
||||
CCoinsViewCache *pcoinsTip = NULL;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -450,9 +451,8 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
|
||||
CBlock blockTmp;
|
||||
|
||||
if (pblock == NULL) {
|
||||
CCoinsDB coinsdb("r");
|
||||
CCoins coins;
|
||||
if (coinsdb.ReadCoins(GetHash(), coins)) {
|
||||
if (pcoinsTip->GetCoins(GetHash(), coins)) {
|
||||
CBlockIndex *pindex = FindBlockByHeight(coins.nHeight);
|
||||
if (pindex) {
|
||||
if (!blockTmp.ReadFromDisk(pindex))
|
||||
@@ -609,7 +609,7 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
|
||||
}
|
||||
}
|
||||
|
||||
bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs,
|
||||
bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs,
|
||||
bool* pfMissingInputs)
|
||||
{
|
||||
if (pfMissingInputs)
|
||||
@@ -668,9 +668,7 @@ bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs,
|
||||
|
||||
if (fCheckInputs)
|
||||
{
|
||||
CCoinsViewDB viewDB(coinsdb);
|
||||
CCoinsViewMemPool viewMemPool(viewDB, mempool);
|
||||
CCoinsViewCache view(viewMemPool);
|
||||
CCoinsViewCache &view = *pcoinsTip;
|
||||
|
||||
// do we already have it?
|
||||
if (view.HaveCoins(hash))
|
||||
@@ -758,9 +756,9 @@ bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTransaction::AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs, bool* pfMissingInputs)
|
||||
bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool* pfMissingInputs)
|
||||
{
|
||||
return mempool.accept(coinsdb, *this, fCheckInputs, pfMissingInputs);
|
||||
return mempool.accept(*this, fCheckInputs, pfMissingInputs);
|
||||
}
|
||||
|
||||
bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
|
||||
@@ -849,31 +847,24 @@ int CMerkleTx::GetBlocksToMaturity() const
|
||||
}
|
||||
|
||||
|
||||
bool CMerkleTx::AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs)
|
||||
bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs)
|
||||
{
|
||||
if (fClient)
|
||||
{
|
||||
if (!IsInMainChain() && !ClientCheckInputs())
|
||||
return false;
|
||||
return CTransaction::AcceptToMemoryPool(coinsdb, false);
|
||||
return CTransaction::AcceptToMemoryPool(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CTransaction::AcceptToMemoryPool(coinsdb, fCheckInputs);
|
||||
return CTransaction::AcceptToMemoryPool(fCheckInputs);
|
||||
}
|
||||
}
|
||||
|
||||
bool CMerkleTx::AcceptToMemoryPool()
|
||||
|
||||
|
||||
bool CWalletTx::AcceptWalletTransaction(bool fCheckInputs)
|
||||
{
|
||||
CCoinsDB coinsdb("r");
|
||||
return AcceptToMemoryPool(coinsdb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CWalletTx::AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs)
|
||||
{
|
||||
|
||||
{
|
||||
LOCK(mempool.cs);
|
||||
// Add previous supporting transactions first
|
||||
@@ -882,20 +873,15 @@ bool CWalletTx::AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs)
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
uint256 hash = tx.GetHash();
|
||||
if (!mempool.exists(hash) && !coinsdb.HaveCoins(hash))
|
||||
tx.AcceptToMemoryPool(coinsdb, fCheckInputs);
|
||||
if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash))
|
||||
tx.AcceptToMemoryPool(fCheckInputs);
|
||||
}
|
||||
}
|
||||
return AcceptToMemoryPool(coinsdb, fCheckInputs);
|
||||
return AcceptToMemoryPool(fCheckInputs);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CWalletTx::AcceptWalletTransaction()
|
||||
{
|
||||
CCoinsDB coinsdb("r");
|
||||
return AcceptWalletTransaction(coinsdb);
|
||||
}
|
||||
|
||||
// Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock
|
||||
bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow)
|
||||
@@ -915,8 +901,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock
|
||||
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
|
||||
int nHeight = -1;
|
||||
{
|
||||
CCoinsDB coindb("r");
|
||||
CCoinsViewDB view(coindb);
|
||||
CCoinsViewCache &view = *pcoinsTip;
|
||||
CCoins coins;
|
||||
if (view.GetCoins(hash, coins))
|
||||
nHeight = coins.nHeight;
|
||||
@@ -1565,18 +1550,15 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck
|
||||
|
||||
bool CBlock::SetBestChain(CBlockIndex* pindexNew)
|
||||
{
|
||||
// if this functions exits prematurely, the transaction is aborted
|
||||
CCoinsDB coinsdb;
|
||||
if (!coinsdb.TxnBegin())
|
||||
return error("SetBestChain() : TxnBegin failed");
|
||||
CCoinsViewCache &view = *pcoinsTip;
|
||||
|
||||
// special case for attaching the genesis block
|
||||
// note that no ConnectBlock is called, so its coinbase output is non-spendable
|
||||
if (pindexGenesisBlock == NULL && pindexNew->GetBlockHash() == hashGenesisBlock)
|
||||
{
|
||||
coinsdb.WriteHashBestChain(pindexNew->GetBlockHash());
|
||||
if (!coinsdb.TxnCommit())
|
||||
return error("SetBestChain() : TxnCommit failed");
|
||||
view.SetBestBlock(pindexNew);
|
||||
if (!view.Flush())
|
||||
return false;
|
||||
pindexGenesisBlock = pindexNew;
|
||||
pindexBest = pindexNew;
|
||||
hashBestChain = pindexNew->GetBlockHash();
|
||||
@@ -1585,10 +1567,6 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew)
|
||||
return true;
|
||||
}
|
||||
|
||||
// create cached view to the coins database
|
||||
CCoinsViewDB viewDB(coinsdb);
|
||||
CCoinsViewCache view(viewDB);
|
||||
|
||||
// Find the fork (typically, there is none)
|
||||
CBlockIndex* pfork = view.GetBestBlock();
|
||||
CBlockIndex* plonger = pindexNew;
|
||||
@@ -1625,8 +1603,11 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew)
|
||||
CBlock block;
|
||||
if (!block.ReadFromDisk(pindex))
|
||||
return error("SetBestBlock() : ReadFromDisk for disconnect failed");
|
||||
if (!block.DisconnectBlock(pindex, view))
|
||||
CCoinsViewCache viewTemp(view, true);
|
||||
if (!block.DisconnectBlock(pindex, viewTemp))
|
||||
return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
|
||||
if (!viewTemp.Flush())
|
||||
return error("SetBestBlock() : Cache flush failed after disconnect");
|
||||
|
||||
// Queue memory transactions to resurrect
|
||||
BOOST_FOREACH(const CTransaction& tx, block.vtx)
|
||||
@@ -1646,10 +1627,13 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew)
|
||||
return error("SetBestBlock() : ReadFromDisk for connect failed");
|
||||
pblock = █
|
||||
}
|
||||
if (!pblock->ConnectBlock(pindex, view)) {
|
||||
CCoinsViewCache viewTemp(view, true);
|
||||
if (!pblock->ConnectBlock(pindex, viewTemp)) {
|
||||
InvalidChainFound(pindexNew);
|
||||
return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
|
||||
}
|
||||
if (!viewTemp.Flush())
|
||||
return error("SetBestBlock() : Cache flush failed after connect");
|
||||
|
||||
// Queue memory transactions to delete
|
||||
BOOST_FOREACH(const CTransaction& tx, pblock->vtx)
|
||||
@@ -1657,11 +1641,10 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew)
|
||||
}
|
||||
|
||||
// Make sure it's successfully written to disk before changing memory structure
|
||||
if (!view.Flush())
|
||||
return error("SetBestBlock() : failed to write coin changes");
|
||||
if (!coinsdb.TxnCommit())
|
||||
return error("SetBestBlock() : TxnCommit failed");
|
||||
coinsdb.Close();
|
||||
bool fIsInitialDownload = IsInitialBlockDownload();
|
||||
if (!fIsInitialDownload || view.GetCacheSize()>5000)
|
||||
if (!view.Flush())
|
||||
return false;
|
||||
|
||||
// At this point, all changes have been done to the database.
|
||||
// Proceed by updating the memory structures.
|
||||
@@ -1678,14 +1661,13 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew)
|
||||
|
||||
// Resurrect memory transactions that were in the disconnected branch
|
||||
BOOST_FOREACH(CTransaction& tx, vResurrect)
|
||||
tx.AcceptToMemoryPool(coinsdb, false);
|
||||
tx.AcceptToMemoryPool(false);
|
||||
|
||||
// Delete redundant memory transactions that are in the connected branch
|
||||
BOOST_FOREACH(CTransaction& tx, vDelete)
|
||||
mempool.remove(tx);
|
||||
|
||||
// Update best block in wallet (so we can detect restored wallets)
|
||||
bool fIsInitialDownload = IsInitialBlockDownload();
|
||||
if (!fIsInitialDownload)
|
||||
{
|
||||
const CBlockLocator locator(pindexNew);
|
||||
@@ -1765,11 +1747,8 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
|
||||
return false;
|
||||
|
||||
// New best
|
||||
if (pindexNew->bnChainWork > bnBestChainWork) {
|
||||
if (!IsInitialBlockDownload() || (pindexNew->nHeight % 1) == 0)
|
||||
if (!SetBestChain(pindexNew))
|
||||
return false;
|
||||
}
|
||||
if (!SetBestChain(pindexNew))
|
||||
return false;
|
||||
|
||||
if (pindexNew == pindexBest)
|
||||
{
|
||||
@@ -2169,11 +2148,9 @@ bool LoadBlockIndex(bool fAllowNew)
|
||||
// Load block index
|
||||
//
|
||||
CChainDB chaindb("cr");
|
||||
CCoinsDB coinsdb("cr");
|
||||
if (!LoadBlockIndex(coinsdb, chaindb))
|
||||
if (!LoadBlockIndex(chaindb))
|
||||
return false;
|
||||
chaindb.Close();
|
||||
coinsdb.Close();
|
||||
|
||||
//
|
||||
// Init with genesis block
|
||||
@@ -2492,7 +2469,7 @@ string GetWarnings(string strFor)
|
||||
//
|
||||
|
||||
|
||||
bool static AlreadyHave(CCoinsDB &coinsdb, const CInv& inv)
|
||||
bool static AlreadyHave(const CInv& inv)
|
||||
{
|
||||
switch (inv.type)
|
||||
{
|
||||
@@ -2504,7 +2481,7 @@ bool static AlreadyHave(CCoinsDB &coinsdb, const CInv& inv)
|
||||
txInMap = mempool.exists(inv.hash);
|
||||
}
|
||||
return txInMap || mapOrphanTransactions.count(inv.hash) ||
|
||||
coinsdb.HaveCoins(inv.hash);
|
||||
pcoinsTip->HaveCoins(inv.hash);
|
||||
}
|
||||
case MSG_BLOCK:
|
||||
return mapBlockIndex.count(inv.hash) ||
|
||||
@@ -2748,7 +2725,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
break;
|
||||
}
|
||||
}
|
||||
CCoinsDB coinsdb("r");
|
||||
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
|
||||
{
|
||||
const CInv &inv = vInv[nInv];
|
||||
@@ -2757,7 +2733,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
return true;
|
||||
pfrom->AddInventoryKnown(inv);
|
||||
|
||||
bool fAlreadyHave = AlreadyHave(coinsdb, inv);
|
||||
bool fAlreadyHave = AlreadyHave(inv);
|
||||
if (fDebug)
|
||||
printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new");
|
||||
|
||||
@@ -2929,7 +2905,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
vector<uint256> vWorkQueue;
|
||||
vector<uint256> vEraseQueue;
|
||||
CDataStream vMsg(vRecv);
|
||||
CCoinsDB coinsdb("r");
|
||||
CTransaction tx;
|
||||
vRecv >> tx;
|
||||
|
||||
@@ -2937,7 +2912,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
pfrom->AddInventoryKnown(inv);
|
||||
|
||||
bool fMissingInputs = false;
|
||||
if (tx.AcceptToMemoryPool(coinsdb, true, &fMissingInputs))
|
||||
if (tx.AcceptToMemoryPool(true, &fMissingInputs))
|
||||
{
|
||||
SyncWithWallets(tx, NULL, true);
|
||||
RelayMessage(inv, vMsg);
|
||||
@@ -2959,7 +2934,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
CInv inv(MSG_TX, tx.GetHash());
|
||||
bool fMissingInputs2 = false;
|
||||
|
||||
if (tx.AcceptToMemoryPool(coinsdb, true, &fMissingInputs2))
|
||||
if (tx.AcceptToMemoryPool(true, &fMissingInputs2))
|
||||
{
|
||||
printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
|
||||
SyncWithWallets(tx, NULL, true);
|
||||
@@ -3407,11 +3382,10 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
||||
//
|
||||
vector<CInv> vGetData;
|
||||
int64 nNow = GetTime() * 1000000;
|
||||
CCoinsDB coinsdb("r");
|
||||
while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
|
||||
{
|
||||
const CInv& inv = (*pto->mapAskFor.begin()).second;
|
||||
if (!AlreadyHave(coinsdb, inv))
|
||||
if (!AlreadyHave(inv))
|
||||
{
|
||||
if (fDebugNet)
|
||||
printf("sending getdata: %s\n", inv.ToString().c_str());
|
||||
@@ -3621,9 +3595,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
||||
int64 nFees = 0;
|
||||
{
|
||||
LOCK2(cs_main, mempool.cs);
|
||||
CCoinsDB coinsdb("r");
|
||||
CCoinsViewDB viewdb(coinsdb);
|
||||
CCoinsViewCache view(viewdb);
|
||||
CCoinsViewCache view(*pcoinsTip, true);
|
||||
|
||||
// Priority order to process transactions
|
||||
list<COrphan> vOrphan; // list memory doesn't move
|
||||
@@ -3811,7 +3783,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
||||
CBlockIndex indexDummy(*pblock);
|
||||
indexDummy.pprev = pindexPrev;
|
||||
indexDummy.nHeight = pindexPrev->nHeight + 1;
|
||||
CCoinsViewCache viewNew(viewdb);
|
||||
CCoinsViewCache viewNew(*pcoinsTip, true);
|
||||
if (!pblock->ConnectBlock(&indexDummy, viewNew, true))
|
||||
throw std::runtime_error("CreateNewBlock() : ConnectBlock failed");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user