diff options
-rw-r--r-- | README.md | 52 | ||||
-rwxr-xr-x | contrib/macdeploy/macdeployqtplus | 12 | ||||
-rw-r--r-- | src/init.cpp | 34 | ||||
-rw-r--r-- | src/main.cpp | 197 | ||||
-rw-r--r-- | src/main.h | 74 | ||||
-rw-r--r-- | src/net.cpp | 3 | ||||
-rw-r--r-- | src/net.h | 1 | ||||
-rw-r--r-- | src/qt/signverifymessagedialog.cpp | 4 | ||||
-rw-r--r-- | src/qt/signverifymessagedialog.h | 4 | ||||
-rw-r--r-- | src/rpcmining.cpp | 41 | ||||
-rw-r--r-- | src/test/miner_tests.cpp | 45 | ||||
-rw-r--r-- | src/util.cpp | 97 | ||||
-rw-r--r-- | src/util.h | 2 | ||||
-rw-r--r-- | src/wallet.cpp | 5 |
14 files changed, 370 insertions, 201 deletions
@@ -22,21 +22,45 @@ or are controversial. The master branch is regularly built and tested, but is not guaranteed to be completely stable. Tags are regularly created to indicate new -official, stable release versions of Bitcoin. If you would like to -help test the Bitcoin core, please contact QA@BitcoinTesting.org. +official, stable release versions of Bitcoin. -Feature branches are created when there are major new features being -worked on by several people. +Testing +======= -From time to time a pull request will become outdated. If this occurs, and -the pull is no longer automatically mergeable; a comment on the pull will -be used to issue a warning of closure. The pull will be closed 15 days -after the warning if action is not taken by the author. Pull requests closed -in this manner will have their corresponding issue labeled 'stagnant'. +Testing and code review is the bottleneck for development; we get more +pull requests than we can review and test. Please be patient and help +out, and remember this is a security-critical project where any +mistake might cost people lots of money. -Issues with no commits will be given a similar warning, and closed after -15 days from their last activity. Issues closed in this manner will be -labeled 'stale'. +Automated Testing +----------------- -Requests to reopen closed pull requests and/or issues can be submitted to -QA@BitcoinTesting.org.
\ No newline at end of file +Developers are strongly encouraged to write unit tests for new code, +and to submit new unit tests for old code. + +Unit tests for the core code are in src/test/ +To compile and run them: + cd src; make -f makefile.linux test + +Unit tests for the GUI code are in src/qt/test/ +To compile and run them: + qmake BITCOIN_QT_TEST=1 -o Makefile.test bitcoin-qt.pro + make -f Makefile.test + ./Bitcoin-Qt + +Every pull request is built for both Windows and +Linux on a dedicated server, and unit and sanity +tests are automatically run. The binaries +produced may be used for manual QA testing +(a link to them will appear in a comment on the pull request +from 'BitcoinPullTester'). +See https://github.com/TheBlueMatt/test-scripts for the +build/test scripts. + +Manual Quality Assurance (QA) Testing +------------------------------------- + +Large changes should have a test plan, and should be tested +by somebody other than the developer who wrote the code. + +See https://github.com/bitcoin/QA/ for how to create a test plan. diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index 16daa59b08..cc60537589 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -172,7 +172,9 @@ class DeploymentInfo(object): elif os.path.exists(os.path.join(os.path.dirname(parentDir), "share", "qt4", "translations")): # Newer Macports layout self.qtPath = os.path.join(os.path.dirname(parentDir), "share", "qt4") - + else: + self.qtPath = os.getenv("QTDIR", None) + if self.qtPath is not None: pluginPath = os.path.join(self.qtPath, "plugins") if os.path.exists(pluginPath): @@ -242,7 +244,11 @@ def runStrip(binaryPath, verbose): subprocess.check_call(["strip", "-x", binaryPath]) def copyFramework(framework, path, verbose): - fromPath = framework.sourceFilePath + if framework.sourceFilePath.startswith("Qt"): + #standard place for Nokia Qt installer's frameworks + fromPath = "/Library/Frameworks/" + framework.sourceFilePath + else: + fromPath = framework.sourceFilePath toDir = os.path.join(path, framework.destinationDirectory) toPath = os.path.join(toDir, framework.binaryName) @@ -345,7 +351,7 @@ def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose): if pluginDirectory == "designer": # Skip designer plugins continue - elif pluginDirectory == "phonon": + elif pluginDirectory == "phonon" or pluginDirectory == "phonon_backend": # Deploy the phonon plugins only if phonon is in use if not deploymentInfo.usesFramework("phonon"): continue diff --git a/src/init.cpp b/src/init.cpp index 557c23fc92..29062452cd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -300,7 +300,7 @@ std::string HelpMessage() " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" + " -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + - " -checklevel=<n> " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + + " -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n" + " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n" + " -reindex " + _("Rebuild blockchain index from current blk000??.dat files") + "\n" + @@ -729,6 +729,33 @@ bool AppInit2() return InitError(msg); } + // Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/ + filesystem::path blocksDir = GetDataDir() / "blocks"; + if (!filesystem::exists(blocksDir)) + { + filesystem::create_directories(blocksDir); + bool linked = false; + for (unsigned int i = 1; i < 10000; i++) { + filesystem::path source = GetDataDir() / strprintf("blk%04u.dat", i); + if (!filesystem::exists(source)) break; + filesystem::path dest = blocksDir / strprintf("blk%05u.dat", i-1); + try { + filesystem::create_hard_link(source, dest); + printf("Hardlinked %s -> %s\n", source.string().c_str(), dest.string().c_str()); + linked = true; + } catch (filesystem::filesystem_error & e) { + // Note: hardlink creation failing is not a disaster, it just means + // blocks will get re-downloaded from peers. + printf("Error hardlinking blk%04u.dat : %s\n", i, e.what()); + break; + } + } + if (linked) + { + fReindex = true; + } + } + // cache size calculations size_t nTotalCache = GetArg("-dbcache", 25) << 20; if (nTotalCache < (1 << 22)) @@ -752,7 +779,10 @@ bool AppInit2() pblocktree->WriteReindexing(true); if (!LoadBlockIndex()) - return InitError(_("Error loading blkindex.dat")); + return InitError(_("Error loading block/coin databases")); + + if (!VerifyDB()) + return InitError(_("Corrupted database detected. Please restart the client with -reindex.")); // as LoadBlockIndex can take several minutes, it's possible the user // requested to kill bitcoin-qt during the last operation. If so, exit. diff --git a/src/main.cpp b/src/main.cpp index 28fbc8b450..cb7975af0a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -467,28 +467,21 @@ CTransaction::GetLegacySigOpCount() const int CMerkleTx::SetMerkleBranch(const CBlock* pblock) { - if (fClient) - { - if (hashBlock == 0) - return 0; - } - else - { - CBlock blockTmp; - - if (pblock == NULL) { - CCoins coins; - if (pcoinsTip->GetCoins(GetHash(), coins)) { - CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); - if (pindex) { - if (!blockTmp.ReadFromDisk(pindex)) - return 0; - pblock = &blockTmp; - } + CBlock blockTmp; + + if (pblock == NULL) { + CCoins coins; + if (pcoinsTip->GetCoins(GetHash(), coins)) { + CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); + if (pindex) { + if (!blockTmp.ReadFromDisk(pindex)) + return 0; + pblock = &blockTmp; } } + } - if (pblock) { + if (pblock) { // Update the tx's hashBlock hashBlock = pblock->GetHash(); @@ -506,7 +499,6 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) // Fill in merkle branch vMerkleBranch = pblock->GetMerkleBranch(nIndex); - } } // Is the tx in a block that's in the main chain @@ -915,16 +907,7 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs) { - if (fClient) - { - if (!IsInMainChain() && !ClientCheckInputs()) - return false; - return CTransaction::AcceptToMemoryPool(false); - } - else - { - return CTransaction::AcceptToMemoryPool(fCheckInputs); - } + return CTransaction::AcceptToMemoryPool(fCheckInputs); } @@ -1464,23 +1447,24 @@ bool CTransaction::ClientCheckInputs() const -bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view) +bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) { assert(pindex == view.GetBestBlock()); + if (pfClean) + *pfClean = false; + + bool fClean = true; + CBlockUndo blockUndo; - { - CDiskBlockPos pos = pindex->GetUndoPos(); - if (pos.IsNull()) - return error("DisconnectBlock() : no undo data available"); - FILE *file = OpenUndoFile(pos, true); - if (file == NULL) - return error("DisconnectBlock() : undo file not available"); - CAutoFile fileUndo(file, SER_DISK, CLIENT_VERSION); - fileUndo >> blockUndo; - } + CDiskBlockPos pos = pindex->GetUndoPos(); + if (pos.IsNull()) + return error("DisconnectBlock() : no undo data available"); + if (!blockUndo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + return error("DisconnectBlock() : failure reading undo data"); - assert(blockUndo.vtxundo.size() + 1 == vtx.size()); + if (blockUndo.vtxundo.size() + 1 != vtx.size()) + return error("DisconnectBlock() : block and undo data inconsistent"); // undo transactions in reverse order for (int i = vtx.size() - 1; i >= 0; i--) { @@ -1488,13 +1472,15 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view) uint256 hash = tx.GetHash(); // check that all outputs are available - if (!view.HaveCoins(hash)) - return error("DisconnectBlock() : outputs still spent? database corrupted"); + if (!view.HaveCoins(hash)) { + fClean = fClean && error("DisconnectBlock() : outputs still spent? database corrupted"); + view.SetCoins(hash, CCoins()); + } CCoins &outs = view.GetCoins(hash); CCoins outsBlock = CCoins(tx, pindex->nHeight); if (outs != outsBlock) - return error("DisconnectBlock() : added transaction mismatch? database corrupted"); + fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted"); // remove outputs outs = CCoins(); @@ -1502,24 +1488,27 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view) // restore inputs if (i > 0) { // not coinbases const CTxUndo &txundo = blockUndo.vtxundo[i-1]; - assert(txundo.vprevout.size() == tx.vin.size()); + if (txundo.vprevout.size() != tx.vin.size()) + return error("DisconnectBlock() : transaction and undo data inconsistent"); for (unsigned int j = tx.vin.size(); j-- > 0;) { const COutPoint &out = tx.vin[j].prevout; const CTxInUndo &undo = txundo.vprevout[j]; CCoins coins; view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent - if (coins.IsPruned()) { - if (undo.nHeight == 0) - return error("DisconnectBlock() : undo data doesn't contain tx metadata? database corrupted"); + if (undo.nHeight != 0) { + // undo data contains height: this is the last output of the prevout tx being spent + if (!coins.IsPruned()) + fClean = fClean && error("DisconnectBlock() : undo data overwriting existing transaction"); + coins = CCoins(); coins.fCoinBase = undo.fCoinBase; coins.nHeight = undo.nHeight; coins.nVersion = undo.nVersion; } else { - if (undo.nHeight != 0) - return error("DisconnectBlock() : undo data contains unneeded tx metadata? database corrupted"); + if (coins.IsPruned()) + fClean = fClean && error("DisconnectBlock() : undo data adding output to missing transaction"); } if (coins.IsAvailable(out.n)) - return error("DisconnectBlock() : prevout output not spent? database corrupted"); + fClean = fClean && error("DisconnectBlock() : undo data overwriting existing output"); if (coins.vout.size() < out.n+1) coins.vout.resize(out.n+1); coins.vout[out.n] = undo.txout; @@ -1532,7 +1521,12 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view) // move best block pointer to prevout block view.SetBestBlock(pindex->pprev); - return true; + if (pfClean) { + *pfClean = fClean; + return true; + } else { + return fClean; + } } void static FlushBlockFile() @@ -1651,9 +1645,9 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust { if (pindex->GetUndoPos().IsNull()) { CDiskBlockPos pos; - if (!FindUndoPos(pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8)) + if (!FindUndoPos(pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock() : FindUndoPos failed"); - if (!blockundo.WriteToDisk(pos)) + if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash())) return error("ConnectBlock() : CBlockUndo::WriteToDisk failed"); // update nUndoPos in block index @@ -2377,36 +2371,77 @@ bool static LoadBlockIndexDB() BlockHashStr(hashBestChain).c_str(), nBestHeight, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + return true; +} + +bool VerifyDB() { + if (pindexBest == NULL || pindexBest->pprev == NULL) + return true; + // Verify blocks in the best chain - int nCheckLevel = GetArg("-checklevel", 1); + int nCheckLevel = GetArg("-checklevel", 3); int nCheckDepth = GetArg( "-checkblocks", 2500); if (nCheckDepth == 0) nCheckDepth = 1000000000; // suffices until the year 19000 if (nCheckDepth > nBestHeight) nCheckDepth = nBestHeight; + nCheckLevel = std::max(0, std::min(4, nCheckLevel)); printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); - CBlockIndex* pindexFork = NULL; + CCoinsViewCache coins(*pcoinsTip, true); + CBlockIndex* pindexState = pindexBest; + CBlockIndex* pindexFailure = NULL; + int nGoodTransactions = 0; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) break; CBlock block; + // check level 0: read from disk if (!block.ReadFromDisk(pindex)) - return error("LoadBlockIndex() : block.ReadFromDisk failed"); + return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); // check level 1: verify block validity - if (nCheckLevel>0 && !block.CheckBlock()) - { - printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - pindexFork = pindex->pprev; + if (nCheckLevel >= 1 && !block.CheckBlock()) + return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + // check level 2: verify undo validity + if (nCheckLevel >= 2 && pindex) { + CBlockUndo undo; + CDiskBlockPos pos = pindex->GetUndoPos(); + if (!pos.IsNull()) { + if (!undo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + } + } + // check level 3: check for inconsistencies during memory-only disconnect of tip blocks + if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) { + bool fClean = true; + if (!block.DisconnectBlock(pindex, coins, &fClean)) + return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pindexState = pindex->pprev; + if (!fClean) { + nGoodTransactions = 0; + pindexFailure = pindex; + } else + nGoodTransactions += block.vtx.size(); } - // TODO: stronger verifications } - if (pindexFork && !fRequestShutdown) - { - // TODO: reorg back - return error("LoadBlockIndex(): chain database corrupted"); + if (pindexFailure) + return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", pindexBest->nHeight - pindexFailure->nHeight + 1, nGoodTransactions); + + // check level 4: try reconnecting blocks + if (nCheckLevel >= 4) { + CBlockIndex *pindex = pindexState; + while (pindex != pindexBest && !fRequestShutdown) { + pindex = pindex->pnext; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + if (!block.ConnectBlock(pindex, coins)) + return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + } } + printf("No coin database inconsistencies in last %i blocks (%i transactions)\n", pindexBest->nHeight - pindexState->nHeight, nGoodTransactions); + return true; } @@ -3732,12 +3767,13 @@ public: } }; -CBlock* CreateNewBlock(CReserveKey& reservekey) +CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) { // Create new block - auto_ptr<CBlock> pblock(new CBlock()); - if (!pblock.get()) + auto_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate()); + if(!pblocktemplate.get()) return NULL; + CBlock *pblock = &pblocktemplate->block; // pointer for convenience // Create coinbase tx CTransaction txNew; @@ -3748,6 +3784,8 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Add our coinbase tx as first transaction pblock->vtx.push_back(txNew); + pblocktemplate->vTxFees.push_back(-1); // updated at end + pblocktemplate->vTxSigOps.push_back(-1); // updated at end // Largest block you're willing to create: unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); @@ -3925,6 +3963,8 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Added pblock->vtx.push_back(tx); + pblocktemplate->vTxFees.push_back(nTxFees); + pblocktemplate->vTxSigOps.push_back(nTxSigOps); nBlockSize += nTxSize; ++nBlockTx; nBlockSigOps += nTxSigOps; @@ -3959,13 +3999,15 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); + pblocktemplate->vTxFees[0] = -nFees; // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->UpdateTime(pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock); pblock->nNonce = 0; pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; + pblocktemplate->vTxSigOps[0] = pblock->vtx[0].GetLegacySigOpCount(); CBlockIndex indexDummy(*pblock); indexDummy.pprev = pindexPrev; @@ -3975,7 +4017,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); } - return pblock.release(); + return pblocktemplate.release(); } @@ -4118,10 +4160,11 @@ void static BitcoinMiner(CWallet *pwallet) unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; CBlockIndex* pindexPrev = pindexBest; - auto_ptr<CBlock> pblock(CreateNewBlock(reservekey)); - if (!pblock.get()) + auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(reservekey)); + if (!pblocktemplate.get()) return; - IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); + CBlock *pblock = &pblocktemplate->block; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); @@ -4134,7 +4177,7 @@ void static BitcoinMiner(CWallet *pwallet) char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf); char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf); - FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1); + FormatHashBuffers(pblock, pmidstate, pdata, phash1); unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8); @@ -4170,7 +4213,7 @@ void static BitcoinMiner(CWallet *pwallet) assert(hash == pblock->GetHash()); SetThreadPriority(THREAD_PRIORITY_NORMAL); - CheckWork(pblock.get(), *pwalletMain, reservekey); + CheckWork(pblock, *pwalletMain, reservekey); SetThreadPriority(THREAD_PRIORITY_LOWEST); break; } diff --git a/src/main.h b/src/main.h index 8bceffbaf9..3290b16318 100644 --- a/src/main.h +++ b/src/main.h @@ -108,6 +108,8 @@ class CTxUndo; class CCoinsView; class CCoinsViewCache; +struct CBlockTemplate; + /** Register a wallet to receive updates from core */ void RegisterWallet(CWallet* pwalletIn); /** Unregister a wallet from core */ @@ -117,7 +119,7 @@ void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* /** Process an incoming block */ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); /** Check whether enough disk space is available for an incoming block */ -bool CheckDiskSpace(uint64 nAdditionalBytes=0); +bool CheckDiskSpace(uint64 nAdditionalBytes = 0); /** Open a block file (blk?????.dat) */ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); /** Open an undo file (rev?????.dat) */ @@ -126,6 +128,8 @@ FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); /** Load the block tree and coins database from disk */ bool LoadBlockIndex(); +/** Verify consistency of the block and coin databases */ +bool VerifyDB(); /** Print the loaded block tree */ void PrintBlockTree(); /** Find a block by height in the currently-connected chain */ @@ -139,7 +143,7 @@ void ThreadImport(void *parg); /** Run the miner threads */ void GenerateBitcoins(bool fGenerate, CWallet* pwallet); /** Generate a new block, without valid proof-of-work */ -CBlock* CreateNewBlock(CReserveKey& reservekey); +CBlockTemplate* CreateNewBlock(CReserveKey& reservekey); /** Modify the extranonce in a block */ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); /** Do mining precalculation */ @@ -437,7 +441,7 @@ enum CheckSig_mode }; /** The basic transaction that is broadcasted on the network and contained in - * blocks. A transaction can contain multiple inputs and outputs. + * blocks. A transaction can contain multiple inputs and outputs. */ class CTransaction { @@ -544,13 +548,11 @@ public: /** Check for standard transaction types @param[in] mapInputs Map of previous transactions that have outputs we're spending @return True if all inputs (scriptSigs) use only standard transaction forms - @see CTransaction::FetchInputs */ bool AreInputsStandard(CCoinsViewCache& mapInputs) const; /** Count ECDSA signature operations the old-fashioned (pre-0.6) way @return number of sigops this transaction's outputs will produce when spent - @see CTransaction::FetchInputs */ unsigned int GetLegacySigOpCount() const; @@ -558,7 +560,6 @@ public: @param[in] mapInputs Map of previous transactions that have outputs we're spending @return maximum number of sigops required to validate this transaction's inputs - @see CTransaction::FetchInputs */ unsigned int GetP2SHSigOpCount(CCoinsViewCache& mapInputs) const; @@ -583,7 +584,6 @@ public: @param[in] mapInputs Map of previous transactions that have outputs we're spending @return Sum of value of all inputs (scriptSigs) - @see CTransaction::FetchInputs */ int64 GetValueIn(CCoinsViewCache& mapInputs) const; @@ -746,7 +746,7 @@ public: READWRITE(vtxundo); ) - bool WriteToDisk(CDiskBlockPos &pos) + bool WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock) { // Open history file to append CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); @@ -764,6 +764,12 @@ public: pos.nPos = (unsigned int)fileOutPos; fileout << *this; + // calculate & write checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + fileout << hasher.GetHash(); + // Flush stdio buffers and commit to disk before returning fflush(fileout); if (!IsInitialBlockDownload()) @@ -771,6 +777,43 @@ public: return true; } + + bool ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock) + { + // Open history file to read + CAutoFile filein = CAutoFile(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CBlockUndo::ReadFromDisk() : OpenBlockFile failed"); + + // Read block + uint256 hashChecksum; + try { + filein >> *this; + } + catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + + // for compatibility with pre-release code that didn't write checksums to undo data + // TODO: replace by a simply 'filein >> hashChecksum' in the above try block + try { + filein >> hashChecksum; + } catch (std::exception &e) { + hashChecksum = 0; + } + uint32_t hashInit = hashChecksum.Get64(0) & 0xFFFFFFFFUL; + if (hashChecksum == 0 || memcmp(&hashInit, pchMessageStart, 4) == 0) + return true; + + // Verify checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + if (hashChecksum != hasher.GetHash()) + return error("CBlockUndo::ReadFromDisk() : checksum mismatch"); + + return true; + } }; /** pruned version of CTransaction: only retains metadata and unspent transaction outputs @@ -1231,7 +1274,6 @@ public: return hash; } - bool WriteToDisk(CDiskBlockPos &pos) { // Open history file to append @@ -1305,8 +1347,11 @@ public: } - // Undo the effects of this block (with given index) on the UTXO set represented by coins - bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins); + /** Undo the effects of this block (with given index) on the UTXO set represented by coins. + * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean + * will be true if no problems were found. Otherwise, the return value will be false in case + * of problems. Note that in any case, coins may be modified. */ + bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL); // Apply the effects of this block (with given index) on the UTXO set represented by coins bool ConnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); @@ -1975,4 +2020,11 @@ extern CCoinsViewCache *pcoinsTip; /** Global variable that points to the active block tree (protected by cs_main) */ extern CBlockTreeDB *pblocktree; +struct CBlockTemplate +{ + CBlock block; + std::vector<int64_t> vTxFees; + std::vector<int64_t> vTxSigOps; +}; + #endif diff --git a/src/net.cpp b/src/net.cpp index b54f8c15f7..7867c85b7f 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -45,10 +45,9 @@ struct LocalServiceInfo { // // Global state variables // -bool fClient = false; bool fDiscover = true; bool fUseUPnP = false; -uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); +uint64 nLocalServices = NODE_NETWORK; static CCriticalSection cs_mapLocalHost; static map<CNetAddr, LocalServiceInfo> mapLocalHost; static bool vfReachable[NET_MAX] = {}; @@ -86,7 +86,6 @@ enum threadId THREAD_MAX }; -extern bool fClient; extern bool fDiscover; extern bool fUseUPnP; extern uint64 nLocalServices; diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index b3fc69ef0f..371b5ba535 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -55,13 +55,13 @@ void SignVerifyMessageDialog::setModel(WalletModel *model) this->model = model; } -void SignVerifyMessageDialog::setAddress_SM(QString address) +void SignVerifyMessageDialog::setAddress_SM(const QString &address) { ui->addressIn_SM->setText(address); ui->messageIn_SM->setFocus(); } -void SignVerifyMessageDialog::setAddress_VM(QString address) +void SignVerifyMessageDialog::setAddress_VM(const QString &address) { ui->addressIn_VM->setText(address); ui->messageIn_VM->setFocus(); diff --git a/src/qt/signverifymessagedialog.h b/src/qt/signverifymessagedialog.h index 5569c8bf33..2c2677cc0c 100644 --- a/src/qt/signverifymessagedialog.h +++ b/src/qt/signverifymessagedialog.h @@ -20,8 +20,8 @@ public: ~SignVerifyMessageDialog(); void setModel(WalletModel *model); - void setAddress_SM(QString address); - void setAddress_VM(QString address); + void setAddress_SM(const QString &address); + void setAddress_VM(const QString &address); void showTab_SM(bool fShow); void showTab_VM(bool fShow); diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 0591f35392..778e0acbd2 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -103,7 +103,7 @@ Value getwork(const Array& params, bool fHelp) typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t; static mapNewBlock_t mapNewBlock; // FIXME: thread safety - static vector<CBlock*> vNewBlock; + static vector<CBlockTemplate*> vNewBlockTemplate; static CReserveKey reservekey(pwalletMain); if (params.size() == 0) @@ -112,7 +112,7 @@ Value getwork(const Array& params, bool fHelp) static unsigned int nTransactionsUpdatedLast; static CBlockIndex* pindexPrev; static int64 nStart; - static CBlock* pblock; + static CBlockTemplate* pblocktemplate; if (pindexPrev != pindexBest || (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) { @@ -120,9 +120,9 @@ Value getwork(const Array& params, bool fHelp) { // Deallocate old blocks since they're obsolete now mapNewBlock.clear(); - BOOST_FOREACH(CBlock* pblock, vNewBlock) - delete pblock; - vNewBlock.clear(); + BOOST_FOREACH(CBlockTemplate* pblocktemplate, vNewBlockTemplate) + delete pblocktemplate; + vNewBlockTemplate.clear(); } // Clear pindexPrev so future getworks make a new block, despite any failures from here on @@ -134,14 +134,15 @@ Value getwork(const Array& params, bool fHelp) nStart = GetTime(); // Create new block - pblock = CreateNewBlock(reservekey); - if (!pblock) + pblocktemplate = CreateNewBlock(reservekey); + if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); - vNewBlock.push_back(pblock); + vNewBlockTemplate.push_back(pblocktemplate); // Need to update only after we know CreateNewBlock succeeded pindexPrev = pindexPrevNew; } + CBlock* pblock = &pblocktemplate->block; // pointer for convenience // Update nTime pblock->UpdateTime(pindexPrev); @@ -248,7 +249,7 @@ Value getblocktemplate(const Array& params, bool fHelp) static unsigned int nTransactionsUpdatedLast; static CBlockIndex* pindexPrev; static int64 nStart; - static CBlock* pblock; + static CBlockTemplate* pblocktemplate; if (pindexPrev != pindexBest || (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { @@ -261,18 +262,19 @@ Value getblocktemplate(const Array& params, bool fHelp) nStart = GetTime(); // Create new block - if(pblock) + if(pblocktemplate) { - delete pblock; - pblock = NULL; + delete pblocktemplate; + pblocktemplate = NULL; } - pblock = CreateNewBlock(reservekey); - if (!pblock) + pblocktemplate = CreateNewBlock(reservekey); + if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); // Need to update only after we know CreateNewBlock succeeded pindexPrev = pindexPrevNew; } + CBlock* pblock = &pblocktemplate->block; // pointer for convenience // Update nTime pblock->UpdateTime(pindexPrev); @@ -281,7 +283,6 @@ Value getblocktemplate(const Array& params, bool fHelp) Array transactions; map<uint256, int64_t> setTxIndex; int i = 0; - CCoinsViewCache &view = *pcoinsTip; BOOST_FOREACH (CTransaction& tx, pblock->vtx) { uint256 txHash = tx.GetHash(); @@ -306,13 +307,9 @@ Value getblocktemplate(const Array& params, bool fHelp) } entry.push_back(Pair("depends", deps)); - int64_t nSigOps = tx.GetLegacySigOpCount(); - if (tx.HaveInputs(view)) - { - entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(view) - tx.GetValueOut()))); - nSigOps += tx.GetP2SHSigOpCount(view); - } - entry.push_back(Pair("sigops", nSigOps)); + int index_in_template = &tx - pblock->vtx.data(); + entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); + entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template])); transactions.push_back(entry); } diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 4558a76a28..bc2a05a6b3 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -49,19 +49,20 @@ struct { BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { CReserveKey reservekey(pwalletMain); - CBlock *pblock; + CBlockTemplate *pblocktemplate; CTransaction tx; CScript script; uint256 hash; // Simple block creation, nothing special yet: - BOOST_CHECK(pblock = CreateNewBlock(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey)); // We can't make transactions until we have inputs // Therefore, load 100 blocks :) std::vector<CTransaction*>txFirst; for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i) { + CBlock *pblock = &pblocktemplate->block; // pointer for convenience pblock->nVersion = 1; pblock->nTime = pindexBest->GetMedianTimePast()+1; pblock->vtx[0].vin[0].scriptSig = CScript(); @@ -75,10 +76,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) assert(ProcessBlock(NULL, pblock)); pblock->hashPrevBlock = pblock->GetHash(); } - delete pblock; + delete pblocktemplate; // Just to make sure we can still make simple blocks - BOOST_CHECK(pblock = CreateNewBlock(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey)); // block sigops > limit: 1000 CHECKMULTISIG + 1 tx.vin.resize(1); @@ -95,8 +96,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, tx); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblock = CreateNewBlock(reservekey)); - delete pblock; + BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey)); + delete pblocktemplate; mempool.clear(); // block size > limit @@ -115,15 +116,15 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, tx); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblock = CreateNewBlock(reservekey)); - delete pblock; + BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey)); + delete pblocktemplate; mempool.clear(); // orphan in mempool hash = tx.GetHash(); mempool.addUnchecked(hash, tx); - BOOST_CHECK(pblock = CreateNewBlock(reservekey)); - delete pblock; + BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey)); + delete pblocktemplate; mempool.clear(); // child with higher priority than parent @@ -140,8 +141,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 5900000000LL; hash = tx.GetHash(); mempool.addUnchecked(hash, tx); - BOOST_CHECK(pblock = CreateNewBlock(reservekey)); - delete pblock; + BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey)); + delete pblocktemplate; mempool.clear(); // coinbase in mempool @@ -151,8 +152,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 0; hash = tx.GetHash(); mempool.addUnchecked(hash, tx); - BOOST_CHECK(pblock = CreateNewBlock(reservekey)); - delete pblock; + BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey)); + delete pblocktemplate; mempool.clear(); // invalid (pre-p2sh) txn in mempool @@ -169,8 +170,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue -= 1000000; hash = tx.GetHash(); mempool.addUnchecked(hash,tx); - BOOST_CHECK(pblock = CreateNewBlock(reservekey)); - delete pblock; + BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey)); + delete pblocktemplate; mempool.clear(); // double spend txn pair in mempool @@ -183,18 +184,18 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); mempool.addUnchecked(hash, tx); - BOOST_CHECK(pblock = CreateNewBlock(reservekey)); - delete pblock; + BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey)); + delete pblocktemplate; mempool.clear(); // subsidy changing int nHeight = pindexBest->nHeight; pindexBest->nHeight = 209999; - BOOST_CHECK(pblock = CreateNewBlock(reservekey)); - delete pblock; + BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey)); + delete pblocktemplate; pindexBest->nHeight = 210000; - BOOST_CHECK(pblock = CreateNewBlock(reservekey)); - delete pblock; + BOOST_CHECK(pblocktemplate = CreateNewBlock(reservekey)); + delete pblocktemplate; pindexBest->nHeight = nHeight; } diff --git a/src/util.cpp b/src/util.cpp index 576ba50dbb..d8f05cb9fd 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -74,7 +74,7 @@ bool fTestNet = false; bool fNoListen = false; bool fLogTimestamps = false; CMedianFilter<int64> vTimeOffsets(200,0); -bool fReopenDebugLog = false; +volatile bool fReopenDebugLog = false; // Init OpenSSL library multithreading support static CCriticalSection** ppmutexOpenSSL; @@ -195,62 +195,76 @@ uint256 GetRandHash() +// +// OutputDebugStringF (aka printf -- there is a #define that we really +// should get rid of one day) has been broken a couple of times now +// by well-meaning people adding mutexes in the most straightforward way. +// It breaks because it may be called by global destructors during shutdown. +// Since the order of destruction of static/global objects is undefined, +// defining a mutex as a global object doesn't work (the mutex gets +// destroyed, and then some later destructor calls OutputDebugStringF, +// maybe indirectly, and you get a core dump at shutdown trying to lock +// the mutex). + +static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT; +// We use boost::call_once() to make sure these are initialized in +// in a thread-safe manner the first time it is called: +static FILE* fileout = NULL; +static boost::mutex* mutexDebugLog = NULL; + +static void DebugPrintInit() +{ + assert(fileout == NULL); + assert(mutexDebugLog == NULL); + + boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + fileout = fopen(pathDebug.string().c_str(), "a"); + if (fileout) setbuf(fileout, NULL); // unbuffered + + mutexDebugLog = new boost::mutex(); +} -inline int OutputDebugStringF(const char* pszFormat, ...) +int OutputDebugStringF(const char* pszFormat, ...) { - int ret = 0; + int ret = 0; // Returns total number of characters written if (fPrintToConsole) { // print to console va_list arg_ptr; va_start(arg_ptr, pszFormat); - ret = vprintf(pszFormat, arg_ptr); + ret += vprintf(pszFormat, arg_ptr); va_end(arg_ptr); } else if (!fPrintToDebugger) { - // print to debug.log - static FILE* fileout = NULL; + static bool fStartedNewLine = true; + boost::call_once(&DebugPrintInit, debugPrintInitFlag); - if (!fileout) - { + if (fileout == NULL) + return ret; + + boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); + + // reopen the log file, if requested + if (fReopenDebugLog) { + fReopenDebugLog = false; boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; - fileout = fopen(pathDebug.string().c_str(), "a"); - if (fileout) setbuf(fileout, NULL); // unbuffered + if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL) + setbuf(fileout, NULL); // unbuffered } - if (fileout) - { - static bool fStartedNewLine = true; - - // This routine may be called by global destructors during shutdown. - // Since the order of destruction of static/global objects is undefined, - // allocate mutexDebugLog on the heap the first time this routine - // is called to avoid crashes during shutdown. - static boost::mutex* mutexDebugLog = NULL; - if (mutexDebugLog == NULL) mutexDebugLog = new boost::mutex(); - boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); - - // reopen the log file, if requested - if (fReopenDebugLog) { - fReopenDebugLog = false; - boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; - if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL) - setbuf(fileout, NULL); // unbuffered - } - // Debug print useful for profiling - if (fLogTimestamps && fStartedNewLine) - fprintf(fileout, "%s ", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str()); - if (pszFormat[strlen(pszFormat) - 1] == '\n') - fStartedNewLine = true; - else - fStartedNewLine = false; + // Debug print useful for profiling + if (fLogTimestamps && fStartedNewLine) + ret += fprintf(fileout, "%s ", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str()); + if (pszFormat[strlen(pszFormat) - 1] == '\n') + fStartedNewLine = true; + else + fStartedNewLine = false; - va_list arg_ptr; - va_start(arg_ptr, pszFormat); - ret = vfprintf(fileout, pszFormat, arg_ptr); - va_end(arg_ptr); - } + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + ret += vfprintf(fileout, pszFormat, arg_ptr); + va_end(arg_ptr); } #ifdef WIN32 @@ -273,6 +287,7 @@ inline int OutputDebugStringF(const char* pszFormat, ...) { OutputDebugStringA(buffer.substr(line_start, line_end - line_start).c_str()); line_start = line_end + 1; + ret += line_end-line_start; } buffer.erase(0, line_start); } diff --git a/src/util.h b/src/util.h index 8bea0dd2b3..97911d7493 100644 --- a/src/util.h +++ b/src/util.h @@ -138,7 +138,7 @@ extern std::string strMiscWarning; extern bool fTestNet; extern bool fNoListen; extern bool fLogTimestamps; -extern bool fReopenDebugLog; +extern volatile bool fReopenDebugLog; void RandAddSeed(); void RandAddSeedPerfmon(); diff --git a/src/wallet.cpp b/src/wallet.cpp index c07adff6c6..37b86c35b5 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1154,7 +1154,10 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { int64 nCredit = pcoin.first->vout[pcoin.second].nValue; - dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); + //The priority after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + dPriority += (double)nCredit * (pcoin.first->GetDepthInMainChain()+1); } int64 nChange = nValueIn - nValue - nFeeRet; |