From cd861ed0a9081b672016e0f4655a19aad9432632 Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 19 Jan 2014 03:34:13 +0400 Subject: [PATCH] Batch block connection during initial block download This reduces the load on the database engine, as subsequent blocks often update an earlier block's transaction already. --- src/db.cpp | 123 ++++++++++++++++++++++++------------ src/db.h | 20 ++++++- src/init.cpp | 12 +++- src/kernel.cpp | 3 +- src/main.cpp | 152 ++++++++++++++++++-------------------------- src/main.h | 31 +++------ src/miner.cpp | 4 +- src/qt/transactiondesc.cpp | 5 +- src/rpcmining.cpp | 4 +- src/rpcrawtransaction.cpp | 17 ++--- src/wallet.cpp | 36 ++++------- src/wallet.h | 5 +- 12 files changed, 208 insertions(+), 204 deletions(-) diff --git a/src/db.cpp b/src/db.cpp index 13127ba..9fbcc8a 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -4,10 +4,10 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "db.h" -#include "kernel.h" -#include "checkpoints.h" #include "util.h" #include "main.h" +#include "kernel.h" +#include "checkpoints.h" #include #include #include @@ -81,8 +81,8 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_) dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1); dbenv.set_lg_bsize(1048576); dbenv.set_lg_max(10485760); - dbenv.set_lk_max_locks(10000); - dbenv.set_lk_max_objects(10000); + dbenv.set_lk_max_locks(40000); + dbenv.set_lk_max_objects(40000); dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug dbenv.set_flags(DB_AUTO_COMMIT, 1); dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); @@ -285,14 +285,10 @@ static bool IsChainFile(std::string strFile) return false; } -void CDB::Close() +void CDB::Flush() { - if (!pdb) - return; if (activeTxn) - activeTxn->abort(); - activeTxn = NULL; - pdb = NULL; + return; // Flush database activity from memory pool to disk log unsigned int nMinutes = 0; @@ -304,6 +300,18 @@ void CDB::Close() nMinutes = 5; 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); @@ -531,18 +539,6 @@ bool CChainDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) { return Write(make_pair('f', nFile), info); } -bool CChainDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { - return Read(make_pair('f', nFile), info); -} - -bool CChainDB::WriteLastBlockFile(int nFile) { - return Write('l', nFile); -} - -bool CChainDB::ReadLastBlockFile(int &nFile) { - return Read('l', nFile); -} - bool CChainDB::ReadSyncCheckpoint(uint256& hashCheckpoint) { return Read('H', hashCheckpoint); @@ -563,6 +559,55 @@ bool CChainDB::WriteCheckpointPubKey(const string& strPubKey) return Write('K', strPubKey); } + +bool CChainDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { + return Read(make_pair('f', nFile), info); +} + +bool CChainDB::WriteLastBlockFile(int nFile) { + return Write('l', nFile); +} + +bool CChainDB::ReadLastBlockFile(int &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::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 &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::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) { if (hash == 0) @@ -583,7 +628,7 @@ CBlockIndex static * InsertBlockIndex(uint256 hash) return pindexNew; } -bool LoadBlockIndex(CCoinsDB &coindb, CChainDB &chaindb) +bool LoadBlockIndex(CChainDB &chaindb) { if (!chaindb.LoadBlockIndexGuts()) return false; @@ -604,7 +649,8 @@ bool LoadBlockIndex(CCoinsDB &coindb, CChainDB &chaindb) { CBlockIndex* pindex = item.second; pindex->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + pindex->GetBlockTrust(); - // NovaCoin: calculate stake modifier checksum + + // Calculate stake modifier checksum pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex); if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum)) return error("CTxDB::LoadBlockIndex() : Failed stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindex->nHeight, pindex->nStakeModifier); @@ -617,26 +663,23 @@ bool LoadBlockIndex(CCoinsDB &coindb, CChainDB &chaindb) printf("LoadBlockIndex(): last block file: %s\n", infoLastBlockFile.ToString().c_str()); // Load hashBestChain pointer to end of best chain - if (!coindb.ReadHashBestChain(hashBestChain)) + pindexBest = pcoinsTip->GetBestBlock(); + if (pindexBest == NULL) { if (pindexGenesisBlock == NULL) return true; return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded"); } - std::map::iterator it = mapBlockIndex.find(hashBestChain); - if (it == mapBlockIndex.end()) { - return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index"); - } else { - // set 'next' pointers in best chain - CBlockIndex *pindex = it->second; - while(pindex != NULL && pindex->pprev != NULL) { - CBlockIndex *pindexPrev = pindex->pprev; - pindexPrev->pnext = pindex; - pindex = pindexPrev; - } - pindexBest = it->second; - nBestHeight = pindexBest->nHeight; - nBestChainTrust = pindexBest->nChainTrust; + hashBestChain = pindexBest->GetBlockHash(); + nBestHeight = pindexBest->nHeight; + nBestChainTrust = pindexBest->nChainTrust; + + // set 'next' pointers in best chain + CBlockIndex *pindex = pindexBest; + while(pindex != NULL && pindex->pprev != NULL) { + CBlockIndex *pindexPrev = pindex->pprev; + pindexPrev->pnext = pindex; + pindex = pindexPrev; } printf("LoadBlockIndex(): hashBestChain=%s height=%d date=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, @@ -829,8 +872,6 @@ bool CAddrDB::Read(CAddrMan& addr) // use file size to size memory buffer int fileSize = GetFilesize(filein); int dataSize = fileSize - sizeof(uint256); - //Don't try to resize to a negative number if file is small - if ( dataSize < 0 ) dataSize = 0; vector vchData; vchData.resize(dataSize); uint256 hashIn; diff --git a/src/db.h b/src/db.h index 1ac971d..bc6a5a5 100644 --- a/src/db.h +++ b/src/db.h @@ -102,6 +102,7 @@ protected: explicit CDB(const char* pszFile, const char* pszMode="r+"); ~CDB() { Close(); } public: + void Flush(); void Close(); private: CDB(const CDB&); @@ -330,6 +331,23 @@ public: 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 &mapCoins, CBlockIndex *pindex); +}; + + /** Access to the block database (chain.dat) */ class CChainDB : public CDB { @@ -354,7 +372,7 @@ public: }; -bool LoadBlockIndex(CCoinsDB &coinsdb, CChainDB &chaindb); +bool LoadBlockIndex(CChainDB &chaindb); /** Access to the (IP) address database (peers.dat) */ diff --git a/src/init.cpp b/src/init.cpp index 39fba49..ef0067f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -60,6 +60,8 @@ void StartShutdown() #endif } +static CCoinsViewDB *pcoinsdbview; + void Shutdown(void* parg) { static CCriticalSection cs_Shutdown; @@ -84,6 +86,12 @@ void Shutdown(void* parg) nTransactionsUpdated++; bitdb.Flush(false); StopNode(); + { + LOCK(cs_main); + pcoinsTip->Flush(); + delete pcoinsTip; + delete pcoinsdbview; + } bitdb.Flush(true); boost::filesystem::remove(GetPidFile()); UnregisterWallet(pwalletMain); @@ -698,10 +706,12 @@ bool AppInit2() uiInterface.InitMessage(_("Loading block index...")); printf("Loading block index...\n"); nStart = GetTimeMillis(); + pcoinsdbview = new CCoinsViewDB(); + pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + if (!LoadBlockIndex()) return InitError(_("Error loading blkindex.dat")); - // as LoadBlockIndex can take several minutes, it's possible the user // requested to kill bitcoin-qt during the last operation. If so, exit. // As the program has not fully started yet, Shutdown() is possibly overkill. diff --git a/src/kernel.cpp b/src/kernel.cpp index dabb053..6ff8e60 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -345,8 +345,7 @@ bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hash CTransaction txPrev; CCoins coins; - CCoinsDB coindb("r"); - CCoinsViewDB view(coindb); + CCoinsViewCache &view = *pcoinsTip; if (!view.GetCoins(txin.prevout.hash, coins)) return tx.DoS(1, error("CheckProofOfStake() : INFO: read coins for txPrev failed")); // previous transaction not in main chain, may occur during initial download diff --git a/src/main.cpp b/src/main.cpp index 437db1a..a65b31f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -194,6 +194,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 &mapCoins, CBlockIndex *pindex) { return false; } CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } bool CCoinsViewBacked::GetCoins(uint256 txid, CCoins &coins) { return base->GetCoins(txid, coins); } @@ -203,12 +204,7 @@ 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 &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); } CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { } @@ -244,18 +240,24 @@ bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) { return true; } -bool CCoinsViewCache::Flush() { - for (std::map::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 &mapCoins, CBlockIndex *pindex) { + for (std::map::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) { } @@ -275,6 +277,7 @@ bool CCoinsViewMemPool::HaveCoins(uint256 txid) { return mempool.exists(txid) || base->HaveCoins(txid); } +CCoinsViewCache *pcoinsTip = NULL; ////////////////////////////////////////////////////////////////////////////// // @@ -468,11 +471,9 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) else { 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)) @@ -533,7 +534,7 @@ bool CTransaction::CheckTransaction() const if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake()) return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction")); - // NovaCoin: enforce minimum output amount for user transactions until 1 May 2014 04:00:00 GMT + // Enforce minimum output amount for user transactions until 1 May 2014 04:00:00 GMT if (!fTestNet && !IsCoinBase() && !txout.IsEmpty() && nTime < OUTPUT_SWITCH_TIME && txout.nValue < MIN_TXOUT_AMOUNT) return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum")); @@ -611,7 +612,7 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) } } -bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs) +bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs) { if (pfMissingInputs) *pfMissingInputs = false; @@ -673,9 +674,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)) @@ -764,9 +763,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) @@ -854,27 +853,21 @@ 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() -{ - CCoinsDB coinsdb("r"); - return AcceptToMemoryPool(coinsdb); -} - -bool CWalletTx::AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs) +bool CWalletTx::AcceptWalletTransaction(bool fCheckInputs) { { @@ -885,21 +878,15 @@ bool CWalletTx::AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs) if (!(tx.IsCoinBase() || tx.IsCoinStake())) { 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) { @@ -918,8 +905,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; @@ -1441,7 +1427,7 @@ bool CTransaction::CheckInputs(CCoinsView &inputs, enum CheckSig_mode csmode, bo // Coin stake tx earns reward instead of paying fee uint64 nCoinAge; - if (!GetCoinAge(inputs, nCoinAge)) + if (!GetCoinAge(nCoinAge)) return error("CheckInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str()); int64 nStakeReward = GetValueOut() - nValueIn; @@ -1747,18 +1733,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() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) { - 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(); @@ -1767,10 +1750,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; @@ -1807,8 +1786,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) @@ -1828,10 +1810,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) @@ -1839,11 +1824,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. @@ -1860,14 +1844,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); @@ -1927,8 +1910,10 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew) // guaranteed to be in main chain by sync-checkpoint. This rule is // introduced to help nodes establish a consistent view of the coin // age (trust score) of competing branches. -bool CTransaction::GetCoinAge(CCoinsView& inputs, uint64& nCoinAge) const +bool CTransaction::GetCoinAge(uint64& nCoinAge) const { + CCoinsViewCache &inputs = *pcoinsTip; + CBigNum bnCentSecond = 0; // coin age in the unit of cent-seconds nCoinAge = 0; @@ -1965,13 +1950,10 @@ bool CBlock::GetCoinAge(uint64& nCoinAge) const { nCoinAge = 0; - CCoinsDB coindb("r"); - CCoinsViewDB view(coindb); - BOOST_FOREACH(const CTransaction& tx, vtx) { uint64 nTxCoinAge; - if (tx.GetCoinAge(view, nTxCoinAge)) + if (tx.GetCoinAge(nTxCoinAge)) nCoinAge += nTxCoinAge; else return false; @@ -2037,11 +2019,8 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) return false; // New best - if (pindexNew->nChainTrust > nBestChainTrust) { - if (!IsInitialBlockDownload() || (pindexNew->nHeight % 1) == 0) - if (!SetBestChain(pindexNew)) - return false; - } + if (!SetBestChain(pindexNew)) + return false; if (pindexNew == pindexBest) { @@ -2729,11 +2708,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 @@ -3041,7 +3018,7 @@ string GetWarnings(string strFor) // -bool static AlreadyHave(CCoinsDB &coinsdb, const CInv& inv) +bool static AlreadyHave(const CInv& inv) { switch (inv.type) { @@ -3053,7 +3030,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) || @@ -3306,7 +3283,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]; @@ -3315,7 +3291,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"); @@ -3503,7 +3479,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector vWorkQueue; vector vEraseQueue; CDataStream vMsg(vRecv); - CCoinsDB coinsdb("r"); CTransaction tx; vRecv >> tx; @@ -3511,7 +3486,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); RelayTransaction(tx, inv.hash); @@ -3531,7 +3506,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CTransaction& orphanTx = mapOrphanTransactions[orphanTxHash]; bool fMissingInputs2 = false; - if (orphanTx.AcceptToMemoryPool(coinsdb, true, &fMissingInputs2)) + if (orphanTx.AcceptToMemoryPool(true, &fMissingInputs2)) { printf(" accepted orphan tx %s\n", orphanTxHash.ToString().substr(0,10).c_str()); SyncWithWallets(tx, NULL, true); @@ -3983,11 +3958,10 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // vector 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()); diff --git a/src/main.h b/src/main.h index 9eeafcd..b5c770a 100644 --- a/src/main.h +++ b/src/main.h @@ -637,8 +637,8 @@ public: bool CheckTransaction() const; // Try to accept this transaction into the memory pool - bool AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); - bool GetCoinAge(CCoinsView& inputs, uint64& nCoinAge) const; // Get transaction coin age + bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL); + bool GetCoinAge(uint64& nCoinAge) const; // Get transaction coin age protected: static CTxOut GetOutputFor(const CTxIn& input, CCoinsView& mapInputs); }; @@ -1103,8 +1103,7 @@ public: int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } bool IsInMainChain() const { return GetDepthInMainChain() > 0; } int GetBlocksToMaturity() const; - bool AcceptToMemoryPool(CCoinsDB& txdb, bool fCheckInputs=true); - bool AcceptToMemoryPool(); + bool AcceptToMemoryPool(bool fCheckInputs=true); }; @@ -1933,8 +1932,7 @@ public: std::map mapTx; std::map mapNextTx; - bool accept(CCoinsDB& coinsdb, CTransaction &tx, - bool fCheckInputs, bool* pfMissingInputs); + bool accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs); bool addUnchecked(const uint256& hash, CTransaction &tx); bool remove(CTransaction &tx); void clear(); @@ -1979,6 +1977,7 @@ public: // Modify the currently active block index virtual bool SetBestBlock(CBlockIndex *pindex); + virtual bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); }; /** CCoinsView backed by another CCoinsView */ @@ -1995,21 +1994,7 @@ public: CBlockIndex *GetBestBlock(); bool SetBestBlock(CBlockIndex *pindex); void SetBackend(CCoinsView &viewIn); -}; - - -/** 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); + bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); }; /** CCoinsView that adds a memory cache for transactions to another CCoinsView */ @@ -2026,7 +2011,9 @@ public: bool HaveCoins(uint256 txid); CBlockIndex *GetBestBlock(); bool SetBestBlock(CBlockIndex *pindex); + bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); bool Flush(); + unsigned int GetCacheSize(); }; /** CCoinsView that brings transactions from a memorypool into view. @@ -2042,4 +2029,6 @@ public: bool HaveCoins(uint256 txid); }; +extern CCoinsViewCache *pcoinsTip; + #endif diff --git a/src/miner.cpp b/src/miner.cpp index ca5a132..7c494fa 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -166,9 +166,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) 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 vOrphan; // list memory doesn't move diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 32700e3..ccd5ad1 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -234,9 +234,6 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += "
" + tr("Transaction") + ":
"; strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); - CCoinsDB coinsdb("r"); // To fetch source txouts - CCoinsViewDB coins(coinsdb); - strHTML += "
" + tr("Inputs") + ":"; strHTML += "
    "; @@ -247,7 +244,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) COutPoint prevout = txin.prevout; CCoins prev; - if(coins.GetCoins(prevout.hash, prev)) + if(pcoinsTip->GetCoins(prevout.hash, prev)) { if (prevout.n < prev.vout.size()) { diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 6ba5a0d..72b850e 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -366,9 +366,7 @@ Value getblocktemplate(const Array& params, bool fHelp) Array transactions; map setTxIndex; int i = 0; - CCoinsDB coindb("r"); - CCoinsViewDB viewdb(coindb); - CCoinsViewCache view(viewdb); + CCoinsViewCache &view = *pcoinsTip; BOOST_FOREACH (CTransaction& tx, pblock->vtx) { uint256 txHash = tx.GetHash(); diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 203583e..dc32217 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -372,9 +372,8 @@ Value signrawtransaction(const Array& params, bool fHelp) CCoinsViewCache view(viewDummy); { LOCK(mempool.cs); - CCoinsDB coinsdb("r"); - CCoinsViewDB viewDB(coinsdb); - CCoinsViewMemPool viewMempool(viewDB, mempool); + CCoinsViewCache &viewChain = *pcoinsTip; + CCoinsViewMemPool viewMempool(viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { @@ -383,7 +382,7 @@ Value signrawtransaction(const Array& params, bool fHelp) 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: @@ -535,17 +534,13 @@ Value sendrawtransaction(const Array& params, bool fHelp) uint256 hashTx = tx.GetHash(); bool fHave = false; + CCoinsViewCache &view = *pcoinsTip; CCoins existingCoins; { - CCoinsDB coinsdb("r"); - { - CCoinsViewDB coinsviewDB(coinsdb); - CCoinsViewMemPool coinsview(coinsviewDB, mempool); - fHave = coinsview.GetCoins(hashTx, existingCoins); - } + fHave = view.GetCoins(hashTx, existingCoins); if (!fHave) { // push to local node - if (!tx.AcceptToMemoryPool(coinsdb)) + if (!tx.AcceptToMemoryPool()) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); } } diff --git a/src/wallet.cpp b/src/wallet.cpp index 20bedc5..86b4a30 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -821,7 +821,6 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) void CWallet::ReacceptWalletTransactions() { - CCoinsDB coinsdb("r"); bool fRepeat = true; while (fRepeat) { @@ -836,7 +835,7 @@ void CWallet::ReacceptWalletTransactions() CCoins coins; bool fUpdated = false; - bool fNotFound = coinsdb.ReadCoins(wtx.GetHash(), coins); + bool fNotFound = pcoinsTip->GetCoins(wtx.GetHash(), coins); if (!fNotFound || wtx.GetDepthInMainChain() > 0) { // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat @@ -862,7 +861,7 @@ void CWallet::ReacceptWalletTransactions() { // Re-accept any txes of ours that aren't already in a block if (!wtx.IsCoinBase()) - wtx.AcceptWalletTransaction(coinsdb, false); + wtx.AcceptWalletTransaction(false); } } if (fMissing) @@ -874,21 +873,22 @@ void CWallet::ReacceptWalletTransactions() } } -void CWalletTx::RelayWalletTransaction(CCoinsDB& coinsdb) +void CWalletTx::RelayWalletTransaction() { + CCoinsViewCache& coins = *pcoinsTip; BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) { if (!tx.IsCoinBase()) { uint256 hash = tx.GetHash(); - if (!coinsdb.HaveCoins(hash)) + if (!coins.HaveCoins(hash)) RelayTransaction((CTransaction)tx, hash); } } if (!IsCoinBase()) { uint256 hash = GetHash(); - if (!coinsdb.HaveCoins(hash)) + if (!coins.HaveCoins(hash)) { printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); RelayTransaction((CTransaction)*this, hash); @@ -896,12 +896,6 @@ void CWalletTx::RelayWalletTransaction(CCoinsDB& coinsdb) } } -void CWalletTx::RelayWalletTransaction() -{ - CCoinsDB coinsdb("r"); - RelayWalletTransaction(coinsdb); -} - void CWallet::ResendWalletTransactions() { // Do this infrequently and randomly to avoid giving away @@ -922,7 +916,6 @@ void CWallet::ResendWalletTransactions() // Rebroadcast any of our txes that aren't in a block yet printf("ResendWalletTransactions()\n"); - CCoinsDB coinsdb("r"); { LOCK(cs_wallet); // Sort them in chronological order @@ -938,7 +931,7 @@ void CWallet::ResendWalletTransactions() BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) { CWalletTx& wtx = *item.second; - wtx.RelayWalletTransaction(coinsdb); + wtx.RelayWalletTransaction(); } } } @@ -1475,8 +1468,7 @@ bool CWallet::GetStakeWeight(const CKeyStore& keystore, uint64& nMinWeight, uint if (setCoins.empty()) return false; - CCoinsDB coinsdb("r"); - CCoinsViewDB view(coinsdb); + CCoinsViewCache &view = *pcoinsTip; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { CCoins coins; @@ -1573,8 +1565,7 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int int64 nCredit = 0; CScript scriptPubKeyKernel; - CCoinsDB coinsdb("r"); - CCoinsViewDB view(coinsdb); + CCoinsViewCache &view = *pcoinsTip; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { CCoins coins; @@ -1726,10 +1717,7 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int { uint64 nCoinAge; - CCoinsDB coindb("r"); - CCoinsViewDB view(coindb); - - if (!txNew.GetCoinAge(view, nCoinAge)) + if (!txNew.GetCoinAge(nCoinAge)) return error("CreateCoinStake : failed to calculate coin age"); nCredit += GetProofOfStakeReward(nCoinAge, nBits, txNew.nTime); } @@ -2281,13 +2269,13 @@ void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion, bool for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) vCoins.push_back(&(*it).second); - CCoinsDB coinsdb("r"); + CCoinsViewCache &view = *pcoinsTip; BOOST_FOREACH(CWalletTx* pcoin, vCoins) { // Find the corresponding transaction index CCoins coins; - bool fNotFound = coinsdb.ReadCoins(pcoin->GetHash(), coins); + bool fNotFound = view.GetCoins(pcoin->GetHash(), coins); for (unsigned int n=0; n < pcoin->vout.size(); n++) { diff --git a/src/wallet.h b/src/wallet.h index c134a29..a3ead62 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -690,10 +690,7 @@ public: void AddSupportingTransactions(); - bool AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs=true); - bool AcceptWalletTransaction(); - - void RelayWalletTransaction(CCoinsDB& coinsdb); + bool AcceptWalletTransaction(bool fCheckInputs=true); void RelayWalletTransaction(); }; -- 1.7.1