Browse Source

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.
miguelfreitas
Pieter Wuille 13 years ago
parent
commit
ae8bfd12da
  1. 89
      src/db.cpp
  2. 20
      src/db.h
  3. 12
      src/init.cpp
  4. 146
      src/main.cpp
  5. 30
      src/main.h
  6. 5
      src/qt/transactiondesc.cpp
  7. 4
      src/rpcmining.cpp
  8. 17
      src/rpcrawtransaction.cpp
  9. 21
      src/wallet.cpp
  10. 6
      src/wallet.h

89
src/db.cpp

@ -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,

20
src/db.h

@ -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) */

12
src/init.cpp

@ -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"));

146
src/main.cpp

@ -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()
{
CCoinsDB coinsdb("r");
return AcceptToMemoryPool(coinsdb);
}
bool CWalletTx::AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs) bool CWalletTx::AcceptWalletTransaction(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");
} }

30
src/main.h

@ -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

5
src/qt/transactiondesc.cpp

@ -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())
{ {

4
src/rpcmining.cpp

@ -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();

17
src/rpcrawtransaction.cpp

@ -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");
} }
} }

21
src/wallet.cpp

@ -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();
} }
} }
} }

6
src/wallet.h

@ -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();
}; };

Loading…
Cancel
Save