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:
Pieter Wuille 2012-07-06 16:33:34 +02:00
parent 450cbb0944
commit ae8bfd12da
10 changed files with 183 additions and 171 deletions

View file

@ -79,8 +79,8 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_)
dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1); dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1);
dbenv.set_lg_bsize(1048576); dbenv.set_lg_bsize(1048576);
dbenv.set_lg_max(10485760); dbenv.set_lg_max(10485760);
dbenv.set_lk_max_locks(10000); dbenv.set_lk_max_locks(40000);
dbenv.set_lk_max_objects(10000); dbenv.set_lk_max_objects(40000);
dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
dbenv.set_flags(DB_AUTO_COMMIT, 1); dbenv.set_flags(DB_AUTO_COMMIT, 1);
dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1);
@ -279,14 +279,10 @@ static bool IsChainFile(std::string strFile)
return false; return false;
} }
void CDB::Close() void CDB::Flush()
{ {
if (!pdb)
return;
if (activeTxn) if (activeTxn)
activeTxn->abort(); return;
activeTxn = NULL;
pdb = NULL;
// Flush database activity from memory pool to disk log // Flush database activity from memory pool to disk log
unsigned int nMinutes = 0; unsigned int nMinutes = 0;
@ -298,6 +294,18 @@ void CDB::Close()
nMinutes = 5; nMinutes = 5;
bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0); bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0);
}
void CDB::Close()
{
if (!pdb)
return;
if (activeTxn)
activeTxn->abort();
activeTxn = NULL;
pdb = NULL;
Flush();
{ {
LOCK(bitdb.cs_db); LOCK(bitdb.cs_db);
@ -537,6 +545,42 @@ bool CChainDB::ReadLastBlockFile(int &nFile) {
return Read('l', nFile); return Read('l', nFile);
} }
CCoinsViewDB::CCoinsViewDB() : db("cr+") {}
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() {
uint256 hashBestChain;
if (!db.ReadHashBestChain(hashBestChain))
return NULL;
std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hashBestChain);
if (it == mapBlockIndex.end())
return NULL;
return it->second;
}
bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { return db.WriteHashBestChain(pindex->GetBlockHash()); }
bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) {
printf("Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size());
if (!db.TxnBegin())
return false;
bool fOk = true;
for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) {
fOk = db.WriteCoins(it->first, it->second);
if (!fOk)
break;
}
if (fOk)
fOk = db.WriteHashBestChain(pindex->GetBlockHash());
if (!fOk)
db.TxnAbort();
else
fOk = db.TxnCommit();
return fOk;
}
CBlockIndex static * InsertBlockIndex(uint256 hash) CBlockIndex static * InsertBlockIndex(uint256 hash)
{ {
if (hash == 0) if (hash == 0)
@ -557,7 +601,7 @@ CBlockIndex static * InsertBlockIndex(uint256 hash)
return pindexNew; return pindexNew;
} }
bool LoadBlockIndex(CCoinsDB &coindb, CChainDB &chaindb) bool LoadBlockIndex(CChainDB &chaindb)
{ {
if (!chaindb.LoadBlockIndexGuts()) if (!chaindb.LoadBlockIndexGuts())
return false; return false;
@ -587,26 +631,23 @@ bool LoadBlockIndex(CCoinsDB &coindb, CChainDB &chaindb)
printf("LoadBlockIndex(): last block file: %s\n", infoLastBlockFile.ToString().c_str()); printf("LoadBlockIndex(): last block file: %s\n", infoLastBlockFile.ToString().c_str());
// Load hashBestChain pointer to end of best chain // Load hashBestChain pointer to end of best chain
if (!coindb.ReadHashBestChain(hashBestChain)) pindexBest = pcoinsTip->GetBestBlock();
if (pindexBest == NULL)
{ {
if (pindexGenesisBlock == NULL) if (pindexGenesisBlock == NULL)
return true; return true;
return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded"); return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded");
} }
std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hashBestChain); hashBestChain = pindexBest->GetBlockHash();
if (it == mapBlockIndex.end()) { nBestHeight = pindexBest->nHeight;
return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index"); bnBestChainWork = pindexBest->bnChainWork;
} else {
// set 'next' pointers in best chain // set 'next' pointers in best chain
CBlockIndex *pindex = it->second; CBlockIndex *pindex = pindexBest;
while(pindex != NULL && pindex->pprev != NULL) { while(pindex != NULL && pindex->pprev != NULL) {
CBlockIndex *pindexPrev = pindex->pprev; CBlockIndex *pindexPrev = pindex->pprev;
pindexPrev->pnext = pindex; pindexPrev->pnext = pindex;
pindex = pindexPrev; pindex = pindexPrev;
}
pindexBest = it->second;
nBestHeight = pindexBest->nHeight;
bnBestChainWork = pindexBest->bnChainWork;
} }
printf("LoadBlockIndex(): hashBestChain=%s height=%d date=%s\n", printf("LoadBlockIndex(): hashBestChain=%s height=%d date=%s\n",
hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,

View file

@ -102,6 +102,7 @@ protected:
explicit CDB(const char* pszFile, const char* pszMode="r+"); explicit CDB(const char* pszFile, const char* pszMode="r+");
~CDB() { Close(); } ~CDB() { Close(); }
public: public:
void Flush();
void Close(); void Close();
private: private:
CDB(const CDB&); CDB(const CDB&);
@ -330,6 +331,23 @@ public:
bool WriteHashBestChain(uint256 hashBestChain); bool WriteHashBestChain(uint256 hashBestChain);
}; };
/** CCoinsView backed by a CCoinsDB */
class CCoinsViewDB : public CCoinsView
{
protected:
CCoinsDB db;
public:
CCoinsViewDB();
bool GetCoins(uint256 txid, CCoins &coins);
bool SetCoins(uint256 txid, const CCoins &coins);
bool HaveCoins(uint256 txid);
CBlockIndex *GetBestBlock();
bool SetBestBlock(CBlockIndex *pindex);
bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
};
/** Access to the block database (chain.dat) */ /** Access to the block database (chain.dat) */
class CChainDB : public CDB class CChainDB : public CDB
{ {
@ -350,7 +368,7 @@ public:
}; };
bool LoadBlockIndex(CCoinsDB &coinsdb, CChainDB &chaindb); bool LoadBlockIndex(CChainDB &chaindb);
/** Access to the (IP) address database (peers.dat) */ /** Access to the (IP) address database (peers.dat) */

View file

@ -50,6 +50,8 @@ void StartShutdown()
#endif #endif
} }
static CCoinsViewDB *pcoinsdbview;
void Shutdown(void* parg) void Shutdown(void* parg)
{ {
static CCriticalSection cs_Shutdown; static CCriticalSection cs_Shutdown;
@ -74,6 +76,12 @@ void Shutdown(void* parg)
nTransactionsUpdated++; nTransactionsUpdated++;
bitdb.Flush(false); bitdb.Flush(false);
StopNode(); StopNode();
{
LOCK(cs_main);
pcoinsTip->Flush();
delete pcoinsTip;
delete pcoinsdbview;
}
bitdb.Flush(true); bitdb.Flush(true);
boost::filesystem::remove(GetPidFile()); boost::filesystem::remove(GetPidFile());
UnregisterWallet(pwalletMain); UnregisterWallet(pwalletMain);
@ -298,6 +306,7 @@ std::string HelpMessage()
return strUsage; return strUsage;
} }
/** Initialize bitcoin. /** Initialize bitcoin.
* @pre Parameters should be parsed and config file should be read. * @pre Parameters should be parsed and config file should be read.
*/ */
@ -641,6 +650,9 @@ bool AppInit2()
uiInterface.InitMessage(_("Loading block index...")); uiInterface.InitMessage(_("Loading block index..."));
printf("Loading block index...\n"); printf("Loading block index...\n");
nStart = GetTimeMillis(); nStart = GetTimeMillis();
pcoinsdbview = new CCoinsViewDB();
pcoinsTip = new CCoinsViewCache(*pcoinsdbview);
if (!LoadBlockIndex()) if (!LoadBlockIndex())
return InitError(_("Error loading blkindex.dat")); return InitError(_("Error loading blkindex.dat"));

View file

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

View file

@ -583,7 +583,7 @@ public:
bool CheckTransaction() const; bool CheckTransaction() const;
// Try to accept this transaction into the memory pool // Try to accept this transaction into the memory pool
bool AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL);
protected: protected:
static CTxOut GetOutputFor(const CTxIn& input, CCoinsView& mapInputs); static CTxOut GetOutputFor(const CTxIn& input, CCoinsView& mapInputs);
@ -682,6 +682,7 @@ public:
bool WriteToDisk(CDiskBlockPos &pos) bool WriteToDisk(CDiskBlockPos &pos)
{ {
// Open history file to append // Open history file to append
CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION);
if (!fileout) if (!fileout)
@ -995,8 +996,7 @@ public:
int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
bool IsInMainChain() const { return GetDepthInMainChain() > 0; } bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
int GetBlocksToMaturity() const; int GetBlocksToMaturity() const;
bool AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs=true); bool AcceptToMemoryPool(bool fCheckInputs=true);
bool AcceptToMemoryPool();
}; };
@ -1676,8 +1676,7 @@ public:
std::map<uint256, CTransaction> mapTx; std::map<uint256, CTransaction> mapTx;
std::map<COutPoint, CInPoint> mapNextTx; std::map<COutPoint, CInPoint> mapNextTx;
bool accept(CCoinsDB& coinsdb, CTransaction &tx, bool accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs);
bool fCheckInputs, bool* pfMissingInputs);
bool addUnchecked(const uint256& hash, CTransaction &tx); bool addUnchecked(const uint256& hash, CTransaction &tx);
bool remove(CTransaction &tx); bool remove(CTransaction &tx);
void clear(); void clear();
@ -1722,6 +1721,7 @@ public:
// Modify the currently active block index // Modify the currently active block index
virtual bool SetBestBlock(CBlockIndex *pindex); virtual bool SetBestBlock(CBlockIndex *pindex);
virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
}; };
/** CCoinsView backed by another CCoinsView */ /** CCoinsView backed by another CCoinsView */
@ -1738,21 +1738,7 @@ public:
CBlockIndex *GetBestBlock(); CBlockIndex *GetBestBlock();
bool SetBestBlock(CBlockIndex *pindex); bool SetBestBlock(CBlockIndex *pindex);
void SetBackend(CCoinsView &viewIn); void SetBackend(CCoinsView &viewIn);
}; bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
/** CCoinsView backed by a CCoinsDB */
class CCoinsViewDB : public CCoinsView
{
protected:
CCoinsDB &db;
public:
CCoinsViewDB(CCoinsDB &dbIn);
bool GetCoins(uint256 txid, CCoins &coins);
bool SetCoins(uint256 txid, const CCoins &coins);
bool HaveCoins(uint256 txid);
CBlockIndex *GetBestBlock();
bool SetBestBlock(CBlockIndex *pindex);
}; };
/** CCoinsView that adds a memory cache for transactions to another CCoinsView */ /** CCoinsView that adds a memory cache for transactions to another CCoinsView */
@ -1769,7 +1755,9 @@ public:
bool HaveCoins(uint256 txid); bool HaveCoins(uint256 txid);
CBlockIndex *GetBestBlock(); CBlockIndex *GetBestBlock();
bool SetBestBlock(CBlockIndex *pindex); bool SetBestBlock(CBlockIndex *pindex);
bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
bool Flush(); bool Flush();
unsigned int GetCacheSize();
}; };
/** CCoinsView that brings transactions from a memorypool into view. /** CCoinsView that brings transactions from a memorypool into view.
@ -1785,4 +1773,6 @@ public:
bool HaveCoins(uint256 txid); bool HaveCoins(uint256 txid);
}; };
extern CCoinsViewCache *pcoinsTip;
#endif #endif

View file

@ -234,9 +234,6 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>"; strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
CCoinsDB coindb("r"); // To fetch source txouts
CCoinsViewDB coins(coindb);
strHTML += "<br><b>" + tr("Inputs") + ":</b>"; strHTML += "<br><b>" + tr("Inputs") + ":</b>";
strHTML += "<ul>"; strHTML += "<ul>";
@ -247,7 +244,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
COutPoint prevout = txin.prevout; COutPoint prevout = txin.prevout;
CCoins prev; CCoins prev;
if(coins.GetCoins(prevout.hash, prev)) if(pcoinsTip->GetCoins(prevout.hash, prev))
{ {
if (prevout.n < prev.vout.size()) if (prevout.n < prev.vout.size())
{ {

View file

@ -281,9 +281,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
Array transactions; Array transactions;
map<uint256, int64_t> setTxIndex; map<uint256, int64_t> setTxIndex;
int i = 0; int i = 0;
CCoinsDB coindb("r"); CCoinsViewCache &view = *pcoinsTip;
CCoinsViewDB viewdb(coindb);
CCoinsViewCache view(viewdb);
BOOST_FOREACH (CTransaction& tx, pblock->vtx) BOOST_FOREACH (CTransaction& tx, pblock->vtx)
{ {
uint256 txHash = tx.GetHash(); uint256 txHash = tx.GetHash();

View file

@ -339,9 +339,8 @@ Value signrawtransaction(const Array& params, bool fHelp)
CCoinsViewCache view(viewDummy); CCoinsViewCache view(viewDummy);
{ {
LOCK(mempool.cs); LOCK(mempool.cs);
CCoinsDB coinsdb("r"); CCoinsViewCache &viewChain = *pcoinsTip;
CCoinsViewDB viewDB(coinsdb); CCoinsViewMemPool viewMempool(viewChain, mempool);
CCoinsViewMemPool viewMempool(viewDB, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
@ -350,7 +349,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
view.GetCoins(prevHash, coins); // this is certainly allowed to fail view.GetCoins(prevHash, coins); // this is certainly allowed to fail
} }
view.SetBackend(viewDummy); // switch back to avoid locking db/mempool too long view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
} }
// Add previous txouts given in the RPC call: // Add previous txouts given in the RPC call:
@ -502,17 +501,13 @@ Value sendrawtransaction(const Array& params, bool fHelp)
uint256 hashTx = tx.GetHash(); uint256 hashTx = tx.GetHash();
bool fHave = false; bool fHave = false;
CCoinsViewCache &view = *pcoinsTip;
CCoins existingCoins; CCoins existingCoins;
{ {
CCoinsDB coinsdb("r"); fHave = view.GetCoins(hashTx, existingCoins);
{
CCoinsViewDB coinsviewDB(coinsdb);
CCoinsViewMemPool coinsview(coinsviewDB, mempool);
fHave = coinsview.GetCoins(hashTx, existingCoins);
}
if (!fHave) { if (!fHave) {
// push to local node // push to local node
if (!tx.AcceptToMemoryPool(coinsdb)) if (!tx.AcceptToMemoryPool())
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
} }
} }

View file

@ -767,7 +767,6 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
void CWallet::ReacceptWalletTransactions() void CWallet::ReacceptWalletTransactions()
{ {
CCoinsDB coinsdb("r");
bool fRepeat = true; bool fRepeat = true;
while (fRepeat) while (fRepeat)
{ {
@ -782,7 +781,7 @@ void CWallet::ReacceptWalletTransactions()
CCoins coins; CCoins coins;
bool fUpdated = false; bool fUpdated = false;
bool fNotFound = coinsdb.ReadCoins(wtx.GetHash(), coins); bool fNotFound = pcoinsTip->GetCoins(wtx.GetHash(), coins);
if (!fNotFound || wtx.GetDepthInMainChain() > 0) if (!fNotFound || wtx.GetDepthInMainChain() > 0)
{ {
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
@ -808,7 +807,7 @@ void CWallet::ReacceptWalletTransactions()
{ {
// Re-accept any txes of ours that aren't already in a block // Re-accept any txes of ours that aren't already in a block
if (!wtx.IsCoinBase()) if (!wtx.IsCoinBase())
wtx.AcceptWalletTransaction(coinsdb, false); wtx.AcceptWalletTransaction(false);
} }
} }
if (fMissing) if (fMissing)
@ -820,21 +819,22 @@ void CWallet::ReacceptWalletTransactions()
} }
} }
void CWalletTx::RelayWalletTransaction(CCoinsDB& coinsdb) void CWalletTx::RelayWalletTransaction()
{ {
CCoinsViewCache& coins = *pcoinsTip;
BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) BOOST_FOREACH(const CMerkleTx& tx, vtxPrev)
{ {
if (!tx.IsCoinBase()) if (!tx.IsCoinBase())
{ {
uint256 hash = tx.GetHash(); uint256 hash = tx.GetHash();
if (!coinsdb.HaveCoins(hash)) if (!coins.HaveCoins(hash))
RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx);
} }
} }
if (!IsCoinBase()) if (!IsCoinBase())
{ {
uint256 hash = GetHash(); uint256 hash = GetHash();
if (!coinsdb.HaveCoins(hash)) if (!coins.HaveCoins(hash))
{ {
printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str());
RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this);
@ -842,12 +842,6 @@ void CWalletTx::RelayWalletTransaction(CCoinsDB& coinsdb)
} }
} }
void CWalletTx::RelayWalletTransaction()
{
CCoinsDB coinsdb("r");
RelayWalletTransaction(coinsdb);
}
void CWallet::ResendWalletTransactions() void CWallet::ResendWalletTransactions()
{ {
// Do this infrequently and randomly to avoid giving away // Do this infrequently and randomly to avoid giving away
@ -868,7 +862,6 @@ void CWallet::ResendWalletTransactions()
// Rebroadcast any of our txes that aren't in a block yet // Rebroadcast any of our txes that aren't in a block yet
printf("ResendWalletTransactions()\n"); printf("ResendWalletTransactions()\n");
CCoinsDB coinsdb("r");
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
// Sort them in chronological order // Sort them in chronological order
@ -884,7 +877,7 @@ void CWallet::ResendWalletTransactions()
BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted)
{ {
CWalletTx& wtx = *item.second; CWalletTx& wtx = *item.second;
wtx.RelayWalletTransaction(coinsdb); wtx.RelayWalletTransaction();
} }
} }
} }

View file

@ -659,11 +659,7 @@ public:
int GetRequestCount() const; int GetRequestCount() const;
void AddSupportingTransactions(); void AddSupportingTransactions();
bool AcceptWalletTransaction(bool fCheckInputs=true);
bool AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs=true);
bool AcceptWalletTransaction();
void RelayWalletTransaction(CCoinsDB& coinsdb);
void RelayWalletTransaction(); void RelayWalletTransaction();
}; };